Groovy - Understand Closure in More Detail and Distinguish Each Lexical Scope in Closure
Repository
https://github.com/JetBrains/intellij-community
https://github.com/gradle/gradle
https://github.com/apache/groovy
What Will I Learn?
- You will learn more about Closure
- Deal with functional programming in Groovy
- All about lexical scope upon Closure in Groovy
Requirements
- The latest version of Java™ SE Development Kit 8
- The latest version of Gradle Build Tool
- The latest stable version of Apache Groovy
- IntelliJ IDEA Community Edition
- Understanding of my previous tutorial
- Clone my current project
https://github.com/murez-nst/JVM-Based.git
and then execute thegradlew build
command on your local directory. Finally, create a new Groovy script,Test
.
I will not discuss the installation and creation steps anymore in this tutorial. For more details, please visit my previous one.
Difficulty
- Beginner
In my previous tutorial, I've already discussed about Groovy DSL can be more flexible and powerful to build such a custom syntax, so anyone can easily interpret the codes even to who doesn't understand the programming at all.
But, I think DSL comes prematurely to the beginners. Because to get closer, they have to understand Closure extensively in advance. And now, this tutorial will explain it in detail. Initially, I will use the term of block to simply refer to the block of statements or algorithm to solve a particular case, i.e. function, method or closure. Because in Groovy, they have the same major characteristic as performing a particular process depends on the parameter(s) (as input), has the type which is a type of the returned value (as output) and always produce any output, at least null
or even you somehow define it which does not intend to return any value at all.
The main goal of a block is to prevent redundant definitions. So, if Java SE has provided a solution raising a number to the power of the second one, Math.pow(double, double)
, then we don't need to create the same new definition. Just use the existing one.
Now, they can be distinguished from each other very easily as follows
Function
The base form of a block that only depends on parameter(s) and the other same level members. The term of function in the world of programming is general and comprehensive meaning, sometimes in a particular language it has a specific meaning. Java was originally intended for object-oriented programming, although it has finally adopted functional programming. So, I think the right definition for a function in the context of Java is the static one which is defined using the static
keyword.
class A {
private final NON_STATIC_RESOURCE = 'will never be touched'
static void sayHello(def yourName) { // (1)
new Inner() // (2)
new Callable() { // (3)
@Override
void to(String name) { }
}
println "Hello ${ yourName }!" // (4)
}
class Inner { }
interface Callable {
void to(String name)
}
}
def result = A.sayHello('World')
println result // (5)
- A function which is defined using
static
keyword. - An error occurs, because this function attempts to access non-static members. This is the meaning of my statement about "a block that only depends on parameter(s) and the other same level members".
- But, why this one can be accessed?
Because as a member inner-interface is always static implicitly.
Because of this rule, we can perform functional programming easily as follows,class A { ... static def greeting(String salaam) { new Callable() { @Override void to(String name) { println "$salaam $name!" } } } } def sayHello = A.greeting('Hello') def sayHi = A.greeting('Hi') sayHello.to 'World' sayHello.to 'Groovy' sayHi.to 'Murez'
- As I explained, a function depends on parameter(s) and the other static members only. If you try to do the following,
println "Hello ${ NON_STATIC_RESOURCE }!"
an error will occur. - The result is
null
, why?
Again, this is the meaning of my statement about "a block always produce any output, at least null or even you somehow define it which does not intend to return any value at all".
Here thesayHello
method with avoid
type is executed, then anull
value will be given.
Method
It is the same as a function but can access non-static members. This is why a method is always represented as a behavior on an object.
In the JVM-based context, a function will never access a field (or sometimes as an attribute or property) which is a non-static member. So, it is ridiculous to create an object first and then execute a function. This is why we only have to do A.sayHello('World')
in order to execute a function as in the previous example.
While in the object-oriented programming context, a method in any class always depends on the fields to perform such a particular process. So in order to execute a method, we must create the object first.
class B {
private String salaam
void say(String name) {
println "$salaam $name!" // (1)
}
}
def withHello = new B(salaam: 'Hello')
def withHi = new B(salaam: 'Hi')
withHello.say 'World' // (2)
withHi.say 'Murez'
- Now a method can access any non-static member, of course by eliminating the
static
keyword. - The
say
method must be invoked by first creating an object of enclosing class.
Eventually why do you have to define a method while there are no dependencies with any property? Then you have to redefine it into a function.
Sometimes, if referring to any name is ambiguous, we can use this
keyword explicitly that corresponds to the enclosing class where it is used.
class Outer {
class Inner {
private String desc
void set(String desc) {
this.desc = desc // (1)
}
}
void get() { this.desc } // (2)
}
- The name of
desc
is ambiguous between field or argument. So,this
used here corresponds to theInner
class and thenthis.desc
is a property. - The
this
used here corresponds to theOuter
class while the field ofdesc
never exists in theOuter
class finally this statement will cause an error
Of course this
cannot be used in a static context, because this
only refers to any non-static member.
Closure
This is the most powerful one, in addition to parameter(s) it also depends on the undefined members which can be easily resolved using delegation, as long as the delegated object defines the missing members correctly.
In Java, any interface that has only one abstract method can be applied as a lambda expression which is an anonymous method, while closure is a lambda expression plus delegate capability.
Closure is an abstract class, but we can create it in the same way as easy as lambda expression. This is the simplest closure, { }
that returns a null
. Delegation can be done by simply invoking the setDelegate
method as follows,
class Any {
private myName
String getName() { myName } // (1)
}
def say = {
println "$it ${ name }!" // (2)
}
say.delegate = new Any(myName: 'World') // (3)
say 'Hello' // (4)
say.delegate = new Any(myName: 'Murez')
say 'Hi'
This class defines a member which is a method named
getName
.Create a closure that will print an argument as
it
and then join with the other undefined member. If we immediately execute this closure, then an error will occur because thename
is completely undefined.We delegate an object to this closure by passing it a new instance of the
Any
class and then give a string to the constructor. This technique is called Named argument constructor one of the features provided by Groovy and we'll not discuss it in this tutorial.There is no error. Because we have delegated an object that has defined a
name()
method.But instead of defining the
getName()
method, should it define thename()
one?Here
myName
is a property ofAny
and its getter isgetName
. Then in Groovy, the getter can be invoked in two ways, as.getName()
or just.name
.Even so about the setter, such as a property of the delegate at the closure which can be invoked as
.setDelegate(object)
or.delegate = object
as well.
Closure has a generic which is an abstract type of a value that will be returned by executing a closure. It can be executed like a function in general, but actually the call
method is implicitly invoked. Sometimes we have to call him explicitly to avoid ambiguity, like the following example
class C {
static def isPositive = { it > 0 }
static def isPositive(def number) {
throw new UnsupportedOperationException()
}
static def test(def value) {
isPositive(value) // (1)
}
}
println C.test(1)
- We will get an exceptions with statement like this. But by changing it to
isPositive.call(value)
, then it is considered as a closure rather than a function.
Lexical Scope this
and owner
If a function can't refer to any class member (except the static ones) or we can define it as does not have a lexical scope, then a method has a lexical scope of this
which references to an enclosing class. Then closures other than having this
also have a lexical scope of owner
and delegate
.
If this
corresponds to an enclosing class, then owner
corresponds to the enclosing of either a class or a closure where it is defined. While delegate
as we know, it corresponds to the delegated object, so we don't need to discuss about it in more detail.
How could it be?
Yes, Exactly! Because a closure can be defined in another closure while a function or method cannot.
class Outer {
class Inner {
def takeOwner() {
({ getOwner() }) // (1)
}
Closure 'Take a Nested closure'() {
({ // (2)
({ owner })() // (3)
})
}
}
void test() {
def inner = new Inner() // (4)
assert inner.takeOwner()() == inner // (5)
Closure nested = inner.'Take a Nested closure'() // (6)
assert nested() == nested // (7)
assert ({ owner })() == this // (8)
println 'Successful!' // (9)
}
}
new Outer().test()
- The method
takeOwner
returns a closure which returns an object of theowner
.
This equivalent to:
So, if you pass a closure as a return value without first storing it to a variable then you have to surround it with parentheses.def takeOwner() { return { getOwner() } }
- The outer closure.
- The inner closure and immediately execute it. Here we can clearly see that a closure can be defined within another one like the top-level container, such as class or interface.
This equivalent to:Closure 'Take a Nested closure'() { return { def innerClosure = { owner } return innerClosure.call() } }
- The instance of
Inner
class - As we can see, the stand-alone closure or in other words a closure that is not inside another one, the
owner
's characteristic will be the same asthis
, which is returning an instance of enclosing class, i.e.Inner
.
This equivalent to:def closure = inner.takeOwner() assert closure() == inner
- Execute the string named method and an instance of the outer closure.
- Execute the outer closure which will immediately execute the inner one which finally returns an object of the
owner
. Becauseowner
is executed in the context of an inner closure then it returns an instance of enclosing closure which is the outer one. - This closure stands alone in the
Outer
class context, it will automatically becomethis
and return an instance ofOuter
class. - Because all assertions in each condition are correct, "Successful!" will be printed.
Finally
I will express my gratitude in a different way,
Curriculum
Proof of Work Done
https://github.com/murez-nst/JVM-Based/tree/master/Groovy/Lombok
Thank you for your contribution.
Looking forward to your upcoming tutorials.
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Thank you for your review, @portugalcoin!
So far this week you've reviewed 8 contributions. Keep up the good work!
Hey @murez-nst
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
Hi @murez-nst! We are @steem-ua, a new Steem dApp, computing UserAuthority for all accounts on Steem. We are currently in test modus upvoting quality Utopian-io contributions! Nice work!
Congratulations @murez-nst! You received a personal award!
Click here to view your Board of Honor
Congratulations @murez-nst! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!