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.gitand then execute thegradlew buildcommand 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
statickeyword. - 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 thesayHellomethod with avoidtype is executed, then anullvalue 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
statickeyword. - The
saymethod 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
descis ambiguous between field or argument. So,thisused here corresponds to theInnerclass and thenthis.descis a property. - The
thisused here corresponds to theOuterclass while the field ofdescnever exists in theOuterclass 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
itand then join with the other undefined member. If we immediately execute this closure, then an error will occur because thenameis completely undefined.We delegate an object to this closure by passing it a new instance of the
Anyclass 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
myNameis a property ofAnyand 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 = objectas 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
takeOwnerreturns 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
Innerclass - 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. Becauseowneris 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
Outerclass context, it will automatically becomethisand return an instance ofOuterclass. - 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!