Thursday, May 08, 2008

Groovy: The Red Pill (or Metaprogramming : The way to blow the mind of a Buttoned-Down Java Developer) - TS-5572

DISCLAIMER:
These are the tidied up notes I took from the session at the JavaOne 2008 conference in San Francisco, California. There may well be mistakes and ommissions. I will come back and correct them once the conference has completed. However my first priority is to get the information published before the backlog gets too large and swamps me. All comments welcome. Enjoy!

Scott Davis
- http://aboutGroovy.com
- Writing mastering Grails for IBM

Groovy + Java
- Groovy is a dynamic language that runs on the JVM
- Since it is implemented in Java, the two sesmlessly integrate - this is not a "burn the boats" migration
- You can take almost any .java file and rename it .groovy and chances are it will run (though it won't be idiomatic groovy)
- Therefore I can still be a "Java Programmer" - the semantic mis-match is kept to a bare minumum
- As you learn the Groovy idioms, you can get into it slowly

Installing Groovy
- Three steps (Dead easy)
- To add Groovy support to your existing java project you just add the single groovy-all-1.5.4.jar (c.f. spring, hibernate, etc.)

HelloJava == HelloGroovy
- println "Hello Groovy World"
- The Groovy code drops all the need for "public static void main..."
- Compile with grvyc => .class files are output
- Run javap against the class files and we see how the simple groovy code gets to a class file
- Groovy offers optional parentheses and semi-colons
- Groovy supports method pointers
- Groovy == Java

Method Pointers
- say = System.out&println # => Pointer to the method
- say "Hola" # => Hola
- Groovy also supports operator overloading (e.g. '<<' to append to an array or other collection) - load = songs.&add ; load "Yellow Submarine" # => makes "load" a pointer to the '.add' method
- play = System.out&println # => ditto

POGOs
- Plain old groovy object - compiles to a class which extends java.lang.Object
- Drop in replacement for a POJO
- semicolons and mutators (get/set) are missing but then the compiler can generate it for you (Note: this is not in-IDE generation; the code is simply not there. It does not need to be there)
- No returns - the last line of a method is an implicit return (though you can add it if you wish)
- No accessors. All classes and methods are public by default, all attributes are private by default
- Strings: "${title} by ${artist}" # => GStrings which support native string interpolation (c.f. Ant, JSPs, Ruby)
- Vararg, named arg constructors: load new Song(title:"Revolution", artist:"The Beatles") ; load new Song(artist:"Pearl Jam", title:"Alive")

Closures
- Definiton: Hunk of exec code which does not have a corrsponding class (Java you must have an anonymous, inner class)
- hi = {println "Hola"} ; hi() # => Hola
- The ExpandoMetaClass depends on this - it adds methods to classes on the fly and so you need free floating methods
- Groovy Shell (c.f. IRB): groovysh:00> 7.times{ println "Hi"} # => 'times' is a closure bolted onto the 7 which is a java.lang.Integer
- d = new Date() ; d++ ; d++ # => adds a day to d

Metaprogramming
- Dynamically adding new methods on the fly at runtime
- message = "Good Moring, JavaOne"; println message
- message = "Good Moring, JavaOne"; println message.shout # method missing exception. we need to add this method to String
- message = "Good Moring, JavaOne"; String.metaClass.shout = {-> delegate.toUpperCase() }; println message # => 'GOOD MORNING, JAVAONE' We added a new method to String
- How does this work? - the ExpandoMetaClass at the core of groovy is where all method calls hit first before hitting the actual classes. Therefore we can do stuff like adding methods to final classes like String
- message = "Good Moring, JavaOne"; String.metaClass.toUpperCase = {-> delegate.toLowerCase() }; println message # => 'good morning, javaone' We overloaded an existing method on String. This is useful in unit testing because we can re route method calls
- Want to give it a go? Start with it in a unit testing environment. It's a gateway drug. ;-)

- class Ipod { def songs = [] ; ipod() { songs <<> println song }; songs.size() } } # 'def' means I don't care what type it is.
- the 'each' iterator. It's all over the place so use it
- family = [dad:"Chris", mom:"Amanda"] # => java.util.HashMap
- family.each{println it} # => Chris, Amanda # 'it' is like the current pointer from the iterator. You can rename it. c.f. Ruby: family.each do |it| it.println end
- family.mom # => Amanda

Groovy Unit Testing
- class IpodTest extend GroovyTestCase { void testPlay() { def expected = 1 ; assertEquals expected, i.play ; } }

Operator Overloading
- You can do it
- Good for DSLs

InvokeMethod and MethodMissing
- Allows you to call methods which don't existin until you call them
- Grails has this feature. It implements invokeMethod (called every time) and methodMissing (called first time)
- Therefore the methods are created when you want them

No comments: