[TOC] ---------------------------------------------------------------------------------------------------- # Methods Also see [, [3.3. Methods](http://groovy-lang.org/structure.html#_methods)].
project1/methods.groovy
````groovy ```` > In Groovy, the last expression evaluated in the body of a method can be returned without > necessitating the `return` keyword. Especially for short methods and for closures, it's nicer to > omit it for brevity. > > --- [, > [2. Return keyword optional](https://groovy-lang.org/style-guide.html#_return_keyword_optional)] > {.right} ## Introspection Also see [Intermediate Java code introspection](#intermediate_java_code_introspection) and [Running by Java runtime](#running_by_java_runtime). ````shell >groovyc methods.groovy >dir /b | find "methods.class" methods.class >java -cp ".;%GROOVY_HOME%\lib\*" methods 11 7 >java -jar %CFR_HOME%cfr-0.152.jar methods.class > methods.java >del methods.class ```` Here we also ran the generated class with Java runtime to confirm that it works. The result is:
project1/methods.java
````java ... ... } ```` So we see a separate overloaded method was added to provide the default method parameter value feature. ---------------------------------------------------------------------------------------------------- # Closures Also see [, [Closures](http://groovy-lang.org/closures.html)].
project1/closures.groovy
````groovy ```` This may look strange that the global variable `name` is not visible inside the method `sayHello()`, this is investigated [here](#visibility_scope). Interesting thing here is the fact that the `Alice` and the `Bob` classes are different and their `name` attributes are resolved dynamically at run time. Also see [, [3. Delegation strategy](http://groovy-lang.org/closures.html#_delegation_strategy)]. ---------------------------------------------------------------------------------------------------- # Method and closure visibility scope {#visibility_scope}
project1/closures_scopes.groovy
````groovy ```` We are curious about why the global variable `myName` is not visible inside the method `sayHello()`, so want to see what happens under the hood (also see [Intermediate Java code introspection](#intermediate_java_code_introspection)): ````wrapped-shell >groovyc -d=closures_scopes_classes closures_scopes.groovy >cd closures_scopes_classes >dir /b closures_scopes$_run_closure1.class closures_scopes.class >java -cp ".;%GROOVY_HOME%\lib\*" closures_scopes Hello from closure, John! >java -jar %CFR_HOME%cfr-0.152.jar closures_scopes.class > closures_scopes.java >java -jar %CFR_HOME%cfr-0.152.jar closures_scopes$_run_closure1.class > closures_scopes$_run_closure1.java >del closures_scopes$_run_closure1.class >del closures_scopes.class ```` As expected a separate class was generated for the closure.
project1/closures_scopes_classes/closures_scopes$_run_closure1.java
````java ... public final class closures_scopes._run_closure1 extends Closure implements GeneratedClosure { private /* synthetic */ Reference myName; ... public closures_scopes._run_closure1(Object _outerInstance, Object _thisObject, Reference myName) { super(_outerInstance, _thisObject); Reference reference; this.myName = reference = myName; } public Object doCall() { return IndyInterface.bootstrap("invoke", "println", 2, this, new GStringImpl( new Object[]{this.myName.get()}, new String[]{"Hello from closure, ", "!"})); } @Generated public Object getMyName() { return this.myName.get(); } ... } ````
project1/closures_scopes_classes/closures_scopes.java
````java ... public class closures_scopes extends Script { ... public Object run() { Reference myName = new Reference((Object)"John"); public final class _run_closure1 extends Closure implements GeneratedClosure { private /* synthetic */ Reference myName; ... public _run_closure1(Object _outerInstance, Object _thisObject, Reference myName) { super(_outerInstance, _thisObject); Reference reference; this.myName = reference = myName; } public Object doCall() { return IndyInterface.bootstrap("invoke", "println", 2, this, new GStringImpl( new Object[]{this.myName.get()}, new String[]{"Hello from closure, ", "!"})); } @Generated public Object getMyName() { return this.myName.get(); } ... } _run_closure1 myClosure = new _run_closure1((Object)this, (Object)this, myName); IndyInterface.bootstrap("invoke", "sayHello", 2, this); return IndyInterface.bootstrap("invoke", "call", 0, myClosure); } public Object sayHello() { return null; } ... } ```` As we can see the so-called "global variable" `myName` is actually defined as a **local** variable inside the `run()` method. The `sayHello()` method is defined outside the `run()` method and called inside the `run()` method. So it cannot see the `myName` variable. As for the closure, its instance is created inside the `run()` method and is given a reference to the `myName` variable. So when it's invoked it can access the `myName` variable. !!! note As we can see the closure `_run_closure1` is defined twice: in its own class file `closures_scopes$_run_closure1.class` and inside the "main" class file `closures_scopes.class` (recall that they were decompiled). There's code duplication there. This is very likely caused by the inner class **inlining** by ether `groovyc` or decompiler. In actual bytecode there may probably be a top-level class for the closure and a reference to it in the script class. But even if actually inlining was done then the meaning of this investigation are still the same, they are just less clear. The full decompiled Java files are: [closures_scopes.java]() and [closures_scopes$_run_closure1.java](). ## `@Field` annotation As we saw above though closures let us use external variables their implementation is much more complex and probably it's an overkill if only global script variables access is required. Following is an alternative solution: ````groovy import groovy.transform.Field @Field def myName = 'John' def sayHello() { println "Hello from method, $myName!" // now CAN use external variables } def myClosure = {-> println "Hello from closure, $myName!"} // still CAN use external variables sayHello() // prints "Hello from method, John!" myClosure() // prints "Hello from closure, John!" ```` The `@Field` annotation tells the Groovy compiler to turn this variable into a field of the generated script class --- not a local variable inside the `run()` method --- which makes it accessible from both methods and closures. The class file investigation is probably not required. Following are some considerations. - **When to use closures?** Closures are the natural choice when: - We want to keep things simple and inline. Closures automatically capture the variables in the surrounding scope (`run()` method in a script), so they "just work" without extra annotations - We're doing callbacks, passing behavior around. Closures are great when used as parameters or stored for later use - We're scripting, not designing long-term structure. If we're just scripting and not defining reusable, class-like APIs, closures give us quick access to variables with no fuss - **When to Use `@Field` + method.** Methods are better when: - We want better performance and static type checking. Closures carry more overhead (they are objects with captured scope), while methods are statically compiled and faster. IDEs (like IntelliJ) also help us more with methods - We need recursion or better structure. Methods can call themselves, be overloaded, or have annotations like `@Memoized`, which closures can't do. - We want to separate logic and state clearly. Declaring fields with `@Field` and methods gives us something closer to class-like structure --- helpful if our script is getting large - Long things short: - Use closures when you're thinking in terms of "small tasks that use outside variables" - Use methods + `@Field` when you're thinking "structured logic using shared state" ---------------------------------------------------------------------------------------------------- # Curries Also see [, [6.1. Currying](https://groovy-lang.org/closures.html#_currying)] and [, [Curry](https://en.wikipedia.org/wiki/Apache_Groovy#Curry)]. > Usually called *partial application*, this Groovy feature allows closures' parameters to be set > to a default parameter in any of their arguments, creating a new closure with the bound value. > Supplying one argument to the `curry()` method will fix argument one. Supplying `N` arguments > will fix arguments `1..N`. ````groovy ````