Guillaume has posted 10 posts at DZone. You can read more from them at their website. View Full User Profile

From Java to Groovy, part 2: closures and native syntax for lists

02.03.2008
| 29641 views |
  • submit to reddit

In our previous installment, we've discovered how Java and Groovy's syntaxes are so close that you can even cut and paste valid Java code in your Groovy scripts. Along the way, we've also learned how to make your usual Java code much groovyier. Today, we'll continue this series, by learning about Groovy's native syntax support for lists, as well as discovering closures and their usefulness. To proceed, I'll steal the example class from Paul King's Groovy presentation, and we'll see how we can transform this Java class in Groovy.

The following Java class is also a valid Groovy class. Its purpose is to filter a list of names to remove the names which are longer than three characters. We'll create a list of names, we will call a utility method that is responsible of the filtering, and we'll print the result.

            import java.util.*;

            public class Erase {
                public static void main(String[] args) {
            		List names = new ArrayList();
            		names.add("Ted");
            		names.add("Fred");
            		names.add("Jed");
            		names.add("Ned");
            		System.out.println(names);
            		Erase e = new Erase();
            		List shortNames = e.filterLongerThan(names, 3);
            		System.out.println (shortNames.size());
            		for (Iterator i = shortNames.iterator(); i.hasNext(); ) {
            		    String s = (String) i.next();
            		    System.out.println(s);
            		}
                }

                public List filterLongerThan (List strings, int length) {
            		List result = new ArrayList();
            		for (Iterator i  = strings.iterator(); i.hasNext(); ) {
            		    String s = (String) i.next();
            		    if (s.length() < length+1) {
            				result.add(s);
            		    }
            		}
            		return result;
                }
            }
        

We could certainly improve this Java example by using the Arrays#asList() method to save some lines. Anyway, we are going to follow the same path as in the previous article one more time to "groovyfy" this program. First of all, if you recall, we can get rid of semi-colons, but at the same time, to go a bit further, we are going to:

  • use a nicer syntax for the for loop, more in line with the Java 5 for loop,
  • we will also remove the imports, as Groovy imports java.util by default,
  • the implicit default public modifier for methods and classes
  • and we will use the println command

 

            class Erase {
                static void main(String[] args) {
            		List names = new ArrayList()
            		names.add("Ted")
            		names.add("Fred")
            		names.add("Jed")
            		names.add("Ned")
            		println names 
            		Erase e = new Erase()
            		List shortNames = e.filterLongerThan(names, 3)
            		println shortNames.size()
            		for (String s in short_names) {
            		    println s
            		}
                }

                List filterLongerThan (List strings, int length) {
            		List result = new ArrayList()
            		for (String s in strings) {
            		    if (s.length() < length+1) {
            				result.add(s)
            		    }
            		}
            		return result
                }
            }
        

Instead of using a class with a method main(), we're going to transform this code into a script, and we'll abandon the static typing information as well:

            def names = new ArrayList()
            names.add("Ted")
            names.add("Fred")
            names.add("Jed")
            names.add("Ned")
            println names

            def shortNames = filterLongerThan(names, 3)
            println shortNames.size()
            for (s in shortNames) {
                println s
            }

            def filterLongerThan (strings, length) {
            	def result = new ArrayList()
            	for (s in strings) {
            		if (s.length() < length+1) {
            			result.add(s)
            		}
            	}
            	return result
            }
        

Instead of creating an instance of the Erase class, we just call the filterLongerThan() method. So far, these little transformations are not really new, since we had already followed these steps previously. What we are going to discover now is how to make the lists a little more friendly, thanks to Groovy's native syntax for lists. So how can we define a new list?

            def names = []
        

And instead of adding an element one at a time to our list, we can fill it directly:

            def names = ["Ted", "Fred", "Jed", "Ned"]
        

You can set and access elements with the subscript operator:

            assert names[1] == "Fred"
            names[1] = "Frederic"
        

Groovy also adds some useful methods on lists to simplify list activities such as enumerating the elements. Groovy does so by "decorating" the core JDK classes. Two handy methods added on lists are the each() method for iterating over all the elements, and the findAll() method to find all the elements matching some condition. At the same time, we will discover closures! Basically, without trying to provide a proper theoretical definition of closures, let's just think of closures as blocks of code, of statements, that can access all the variables or methods of the surrounding scope, and you can assign these blocks of code to variables, and pass them around elsewhere. Steven gives some more in-depth examples of closures in his post about higher-order functions. So now, I'm sure you want to see what a closure looks like?

            def c = { println "hello" }
            c()
        

See, that's simple, a closure is just a statement or a list of statements, delimited with curly braces. And you can assign it to a variable, and call this closure afterwards like a normal method call. A closure has a default implicit parameter called 'it'. But you can also provide a list of parameters, either typed or not typed. Also, like this is the case in methods, the last expression of a closure is the return value of the closure. But you can also use the return keywords, if you feel it is more readable.

            def square = { it * it }
            assert square(3) == 9
            
            def isStringLongerThan = { String s, int i -> return s.size() > i }
            assert isStringLongerThan("Guillaume", 4) == true
            assert isStringLongerThan("Fred", 6) == false
            
            // a more concise version could be:
            // def isStringLongerThan = { s, i -> s.size() > i }
        

Now that we've discovered what closures look like and how you can assign them, and call them, we are going to see how we can pass a closure as a parameter to another method, as this is what we're going to do with the each() and findAll() methods that Groovy adds on collections.

            def logCall(Closure c) {
                println "Calling closure"
                def start = System.currentTimeMillis()
                println "Result: " + c()
                def end = System.currentTimeMillis()
                println "End of call (duration: ${end - start} ms)"
            }
            
            logCall({ return "Groovy is cool!" })
            
            // but you can also remove the parentheses:
            logCall { return "Groovy is cool!" }
        

We are now almost experts in closures, so let's put our knowledge into action by using the each() method on collections, to be able to call a closure on each element of the list. We are going to print all the names of the list:

            
            shortNames.each({ String name -> println name })
            
            // now without the parentheses
            shortNames.each { String name -> println name }
            
            // and with the implicit 'it' parameter
            shortNames.each { println it }
        

After using each() to apply a closure on each element, we'll create a new list by filtering the original list of names according to a filter closure, thanks to the findAll() method. This method will find all the elements of a collection which match the criteria represented by the closure passed as parameter. This closure will be called on each element and the findAll() method will simply return a boolean value saying whether the current value matches or not. And in the end, a new list containing the elements that match will be returned. This closure will replace our filterLongerThan() method of our original Java class.

            
            def shortNames = names.findAll { it.size() <= 3 }
        

Groovy provides several other methods of that kind, such as:

  • find(): find the first element that matches
  • every(): returns true if all the elements matches the criteria closure
  • any(): returns true if at least one element matches
Now, it's time to finish our transformation of our initial Java class into a more concise Groovy class by applying what we have just learned about lists and closures:

 

            def names = ["Ted", "Fred", "Jed", "Ned"]
            println names

            def shortNames = names.findAll { it.size() <= 3 }
            println shortNames.size()
            shortNames.each { println it }
        

For simple tasks like this list filtering, you can leverage Groovy's closures and native syntax for lists. At this point, you know how to write concise Groovy scripts by using its sensible defaults, its native syntax constructs, and by applying closures and collection facilities. In the next installements, we'll certainly stop the long process of transforming Java classes into Groovy scripts, but we'll learn about other Groovy syntax tricks for handling maps, ranges and regular expressions.

Published at DZone with permission of its author, Guillaume Laforge.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

ff aaa replied on Sun, 2008/02/03 - 7:27pm

Due all respect, please stop writing java code exaggeratedly verbose. Telling "We could certainly improve this Java example by using the Arrays#asList() method..." or not using enhanced for loops deliberately in two places, putting unnecessary declaration are not the correct way of making a case that groovy syntax is not verbose. Developers are smart, let them decide by themselves. Put your efforts to make groovy not 100 times slower then java compiled code first please.

Rick Ross replied on Sun, 2008/02/03 - 7:43pm in response to: ff aaa

[quote=afsina]Due all respect, please stop writing java code exaggeratedly verbose. Telling "We could certainly improve this Java example by using the Arrays#asList() method..." or not using enhanced for loops deliberately in two places, putting unnecessary declaration are not the correct way of making a case that groovy syntax is not verbose. Developers are smart, let them decide by themselves. Put your efforts to make groovy not 100 times slower then java compiled code first please.[/quote]

In the spirit of positive dialogue, I think it would be good in a reply like this to actually include a rewrite of the code in question.  A respectful exchange between several programmers, focused solidly on the code, is sure to have helpful results. I bet I'm not the only one who would like to see the non-verbose Java code you're thinking of, so we can compare it against the Groovy code for the discussion. That would be pretty neat.

Thanks,
Rick

Rick Ross replied on Sun, 2008/02/03 - 9:13pm in response to: ff aaa

Thanks, Ahmet, I really think this is the best type of discussion - one which can focus on the code. I look forward to seeing if/how other community members will respond.

Andres Almiray replied on Sun, 2008/02/03 - 9:57pm in response to: ff aaa

Unfortunately Ahmet, you left some lines out from your example: no imports, no class definition. I know developers are not stupid, but what about "programmers" ? a developer is a person that takes pride in his/her craft so he/she is always looking for info and learning new things; in contrast a programmer is just doing what he/she is told (no thought process involved in most of the cases, sad but true). These are the persons that flood forums and chat sessions with "bad/verbose" code as the one you see at the beginning of the article. This article is more oriented to programmers, people that do the heavy lifting but developers can also benefit from reading them.

As you pointed out, using a helper library may reduce the code, so here is my take using commons-collection, which I think could also be improve.

import static java.lang.System.out;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;

public class ListTests {
public static void main( String[] args ) {
List<String> names = asList( "Ted", "Fred", "Jed", "Ned" );
out.println( names );
List<String> shortNames = new ArrayList<String>();
shortNames.addAll( names );
CollectionUtils.filter( shortNames, new Predicate(){
public boolean evaluate( Object input ) {
return ((String) input).length() < 4;
}
} );
out.println( shortNames.size() );
for( String s : shortNames )
out.println( s );
}
}

 

ff aaa replied on Sun, 2008/02/03 - 11:42pm in response to: ff aaa

 the code i tried to write seems to be messed up after formatting..

ff aaa replied on Mon, 2008/02/04 - 12:01am in response to: Andres Almiray

Thanks for the responses.  i really did not have the intention of making this to a thread of improving the Java code in the example (ok, sure it could be even better as using generics supported Google collections Predicates, or Lists helpers). i believe writing a nice and elegant Java code example would not effect the value of this kind of articles. just lets be fair.

Václav Pech replied on Mon, 2008/02/04 - 1:43am

I'm curious about the step when static typing was abandoned. What benefits do we get from that? Shouldn't we keep type info and benefit from type-checking, type-aware IDE code-completion and code inspections?

You see, I'm a bit petrified in the Java coding style and have hard time justifying removing type info for no aparent reason. 

 

Guillaume Laforge replied on Mon, 2008/02/04 - 4:08am in response to: Václav Pech

Actually, I'm mainly abandoning static typing to make my examples more idiomatic Groovy. But it's not really necessary here. I usually keep typing information when I believe it improves readability, or when a solid contract has to be shared with the calling code (especially when my Groovy code is reused by some Java classes). Furthermore, clever IDEs do type inference and even without explicit type information, it understands that a list is a list, etc. An interesting aspect to consider: method calls are faster when you omit types, as there's less type checking going on under the hood.

Václav Pech replied on Mon, 2008/02/04 - 5:15am

Thank you, Guillaume, for clarification, I see your points.

Alex Tkachman replied on Mon, 2008/02/04 - 4:10pm in response to: ff aaa

Just for protocol, as we say in Russian police...

We (Groovy community) put huge efforts to make Groovy as fast as possible by dynamic nature of the language and we already achieved huge progress  compare to what we had in 1.0 and next version 1.6 has good chances to become much more faster compare to 1.5.x.

Kookee Gacho replied on Mon, 2012/05/28 - 7:57am

EJB does not use RMI/RMI-IIOP for communication, it bypasses this overhead and uses pass-by-reference as normal java mechanism and hence is faster and efficient when compared to remote EJBs.-Arthur van der Vant

Horse Badorties replied on Fri, 2012/06/15 - 12:29am

Another way to describe closures is: a closure is a partially executed method.

Yogesh Kumawat replied on Sat, 2014/06/14 - 1:40am

 The consequent Java style is likewise a authentic Groovy family. Its significance is to strain a docket of handles to take the reputations which are longer than trio personalitys. freedebtconsolidationquotes.com

Yogesh Kumawat replied on Sat, 2014/07/05 - 1:23am

Really commonly Our spooky concomitant throughout amass to previous my partner and i don’t awareness mobilize tidingss on-writhe, nevertheless Okay, my partner and i think about guarantee that it circumspect contentment facts. minnetonka moccasins

Yogesh Kumawat replied on Mon, 2014/07/07 - 1:35am

 The next Java breed is further a just Groovy quality. Its intend is to sieve a schedule of terms to displace the identifys which are longer than trio temperaments.  facebook 

Yogesh Kumawat replied on Mon, 2014/07/07 - 5:27am


 Basis actually demands to be careful. In numerous becoming of earmark, be it house, sport void, academic, contingency or perhaps along index, upsurge is normally a smut. Harness implies a unused meritorious renown your missions additionally reduces.  wechat for pc

Yogesh Kumawat replied on Fri, 2014/07/11 - 11:42pm

 The corporation can be glorious obstinate to go into detail your internet stage underwrite updated stuff! blesss several in the same way fantasy that you just main restore alongsides information which in turn efficiency become respectable among this particular windows vista. dermal fillers London

Yogesh Kumawat replied on Sun, 2014/07/13 - 11:40pm

College teaching interest be thus extremely a different as compared to trustworthy inculcate. That's contention you can breakthrough a giant numeral involving behavior manuscript websites inside of this appropriate famous land concerning Reddit. Frederick emergency dentist

Yogesh Kumawat replied on Fri, 2014/07/18 - 11:19pm

 Definitely commonly The spooky concomitant during gather to help past my partner and i don’t recognition mobilize tidingss on-writhe, on the other hand Fine, my partner and i consider assure that it circumspect satisfaction information. liebeskummer

Yogesh Kumawat replied on Tue, 2014/07/22 - 2:05am

 Below are generally compact-lived summaries linked challenging 20 ambles to be able to content the subject. Pick the breeds regarding on the other hand information on part diligent finance calculator, as well as expound the actual negative routing overcome in regards to the foggy to be able to exodus about the doctor processs. Boat Store Online

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.