Of course, no introduction of myself would be complete without introducing my wife (CLICK!) And my four children (CLICK!)
Consider the following Groovy classâŠ.itâs a Duck, and as any good Duck should, it walks like a duck and quacks like a duck. (CLICK!) Now, consider this Groovy classâŠ.itâs a MentalPatient, and it just so happens to walk like a duck and quack like a duck. (CLICK!) Now, consider the Groovy classâŠ.it claims to walk with ducks. It defines one method, with an interesting type declaration in its signature (CLICK!) Here we have a dynamic type declarationâŠ.âdefââŠ.this allows us to pass in either a Duck or a MentalPatientâŠand to WalksWithDucks, as long as it walks like a duckâŠ. (CLICK!) Itâs a duck!!!
At the end of the day, itâs all in what you respond to! (CLICK!) Here we see one of Groovyâs metaprogramming constructs, the ârespondsToâ method, which allows us to see whether or not a given object responds to certain method calls, irrespective of any class or interface implementation
In Groovy application, 3 kinds of objects: POJOâs (Plain Old Java Objects) -> regular Java objects created in Java or other JVM languages (JRuby, Jython, etc.) POGOâs (Plain Old Groovy Objects) -> written in Groovy, extend java.lang.Object, implement groovy.lang.GroovyObject Groovy interceptors -> written in Groovy and implement GroovyInterceptable invokeMethod(), getProperty(), setProperty() â make Groovy objects highly dynamic â can be used to work w/ methods and properties created on the fly getMetaClass()/setMetaClass() â make it very easy to create proxies to intercept and also inject methods on a POGO
Marker interface that extends GroovyObject All method calls â both existing and nonexisting methods â on an object implementing this interface are intercepted by its invokeMethod()
Groovy allows metaprogramming for POJOâs and POGOâs POJOS â Groovy maintains a MetaClassRegistry class of MetaClasses POGOS â direct reference to their MetaClass When you call a method: Groovy first checks whether the target is a POJO or POGO, and handles such calls differently If it is a POJO, Groovy fetches its MetaClass from the MetaClassRegistry and delegates method invocation to it. Any interceptors or methods youâve defined on its MetaClass take precedence over the original method of the POJO For POGOâs, Groovy takes a few extra steps (CLICK!)
If the class implements GroovyInterceptable, all calls get routed to its invokeMethod() If not, then Groovy looks for the method first in the POGOâs MetaClass, and then if not found, in the POGO itself, executing in that order of preference If the POGO does not have either, it then looks for a property with that method name. If found, and if its type is Closure, it invokes the Closure. If no closure property, it invokes methodMissing() if it exists If not, it invokes the POGOâs invokeMethod() If all else fails, Groovy throws a MissingMethodException indicating the failure of the call
Suppose we have a Car class for which we want to call its check() method to perform filtering prior to any other method call CLICK! Here we implement invokeMethod()âŠ.CLICK! First, we check to see if the name of the method is not check()âŠ.if not, we get access to the check() MetaMethod and invoke itâŠ.CLICK! Next, we try to access the MetaMethod for the method that was calledâŠCLICK! If we donât get a null, we know we have a valid method, and we invoke itâŠCLICK! Otherwise, we delegate the call to the Car MetaClassâs invokeMethod()âŠ.the default implementation throwing a MissingMethodExceptionâŠ.CLICK! Hereâs some client codeâŠwe call the 3 valid methods and then one invalid methodâŠCLICK! And hereâs the outputâŠ.notice that for start() and drive() we run the filterâŠ.but when check() is called directly, we do notâŠ.and again, notice the MissingMethodException on speed()
We can do the same thing using MetaClassâŠthere are a few subtle differences: (CLICK!) We actually implement invokeMethod on the Car MetaClass by implementing a closure and assigning it to that method name. (CLICK!) Instead of passing âthisâ to invoke, we pass âdelegate.â In a closure, delegate refers to the object that owns the closure. (CLICK!) Lastly, we call invokeMissingMethod() on the metaClass since we are already executing invokeMethod() and would not want to call that method recursively. invokeMissingMethod() actually delegates to invokeMethod() in the case of existing methods, and to missingMethod() in the case of nonexisting methods
We can also use MetaClass on a POJOâŠin this case the java.lang.Integer class (CLICK!) Again, weâre implementing a closure and assigning it to invokeMethod on the Integer class (CLICK!) The closure simply checks for a valid method call, invokes pre-filtering code, invokes the method, and invokes post-filtering code. Here is some client codeâŠ.the first two calls exist on Integer, while the third does not⊠(CLICK!) And here is the outputâŠ..notice the execution of the filters for the valid method calls and then the MissingMethodException thrown on the call to empty()
So here weâre going to implement a StringUtil class containing a single method, toSSN(), that weâd like to be vailable on both String and StringBufferâŠit will format the contents of the string as a social security number. So to do category injection, there are a few requirements: (CLICK!) First, you need a static methodâŠ(CLICK!) That accepts at least one parameter (the object for which the method is to be injected) (CLICK!) You write a use block, with the Category class as the argumentâŠin this case, any Object inside the use block will have access to the toSSN() method because we left toSSN() dynamically typed (CLICK!) Objects outside the scope of the use block will not have toSSN()âŠ.(CLICK!) And here is the outputâŠ
I want to show you a second example of this to futher illustrate the flexibilityâŠ.here we have a FindUtil class with an extractOnly() methodâŠ.(CLICK!) Again, the method is staticâŠ.(CLICK!) It takes the object (in this case a String) on which to inject the method as the first parameterâŠ.(CLICK!) And it also takes a second argument, in this case a closure that we expect to return a boolean valueâŠ(CLICK!) Inside the method, we iterate over each character in the String and append to the result only characters for which the closure returns true. Youâll see âitâ here, which happens to be the default argument in closures if none are specifiedâŠ(CLICK!) We setup our use block and execute the code (CLICK!) In this case, we pass in a closre that returns true only if the scanned character is a â4â or a â5ââŠ.given the String of characters we start with, we get 54 as the outputâŠ
Here weâd like to add a methodâŠ.daysFromNow()âŠ.to the Integer classâŠit will return an instance of java.util.Date equal to the Integer number of days from todayâŠ.(CLICK!) Now you can tell I only made this slide a couple of days ago âŠ. :-)
We can also add a property in a similar way by implementing the âgetterâ method for a property called daysFromNowâŠ.this allows us to drop the parenthesis, creating a more âfluentâ or human readable interface, something you like to shoot for when building domain specific languagesâŠCLICK! Hereâs the outputâŠ.
We can also add a static method by implementing methods on the static property of the metaClassâŠhereâs weâll add a method indicating whether or not its argument is an even numberâŠ.CLICK! Again, hereâs the outputâŠ.
The last method type we can add in is a constructorâŠwe do this by appending a closure to the existing set of constructors on IntegerâŠ.in this case, we want to construct an Integer equal to the current day of the yearâŠCLICK! And the outputâŠ.
Letâs define a Person class that plays several sportsâŠCLICK! First weâll implement methodMissing()âŠCLICK! Weâll search for the suffix of the method named calledâŠ.in this case weâre expecting method calls like âplayFootballâ or âplayPoloââŠ.CLICK! Having found it, weâll return a String that says weâre âplayingâ the sportâŠ.here weâre using Groovyâs ability to execute arbitrary Groovy code embedded in a String inside the ${} constructâŠ. CLICK! If we donât find a legal method name, we throw a MissingMethodExceptionâŠ.hereâ some client code with several legal calls and one illegal callâŠ.CLICK! And hereâs the outputâŠ.notice that methodMissing() is not called for the work() method, but is for all of the sports-related methodsâŠ.and we got a MissingMethodException for politics, which is usually not a sport, depending on who youâre talking toâŠ.
Now letâs implement the Intercept, Cache, Invoke pattern on our Person classâŠCLICK! First we intercept the method call, just as we did before. Having found itâŠ.CLICK! We implement a closure and add it to the Personâs MetaClassâŠagain weâre using Groovyâs String evaluation capabilities to dynamically construct the method nameâŠ.CLICK! We then return the result of the closure we just implementedâŠ.CLICK! Quickly notice the static initializer blockâŠ.here Iâm simply âtouchingâ the metaClass for Person. The reason for this is an eccentricity in Groovy in that ExpandoMetaClass is not the default MetaClass for Groovy, however once you access it the first time it is replaced with an instance of EMC. This may be changed in future versionsâŠ.CLICK! Hereâs some client codeâŠCLICK!âŠ.and hereâs the output. Notice that methodMissing() is only called once
So hereâs the beginning of our DSLâŠ.the first thing we do is enable the ExpandoMetaClass globallyâŠ.this causes methods we add to a parent class, in this case Number, to be available to its childrenâŠ.CLICK! Next we reimplement Numberâs get Property methodâŠ.we assign it a closure that uses Jscienceâs API to instantiate an Amount equal to the value of the Number instance and units specified by the propery nameâŠ. Iâve added a few examples of this andâŠ.CLICK!âŠ.hereâs the output
The next step is to define our operator overloadingâŠ.since Jscience doesnât use the same operation names as Groovy, we need to implement a few closures to massage out the differencesâŠ..Iâve put here examples of Multiplication Division Addition Subtraction Powers Opposites And comparisonsâŠand CLICK! Hereâs the outputâŠ..notice on the power example we mixed metric and english units, and Jscience guessed that weâd want the output in metric units (indicated by the question mark)âŠ
Our third step is to allow us to specify inverse units in a clean way, just like a physicist would. In a Groovy script like this one, all variables are local variablesâŠ.however you can also pass in variables through a Binding, allowing for easy integration with existing Java code, etc. Here weâre going to implement a binding that will ignore references to the out property, which represents System.outâŠ.otherwise, it will return an amount equal to 1 unit of the symbol representing the property we tried to accessâŠ. Now, you can see these examples of inverse units, such as kilometers per hour, milligrams per Liter, etcâŠCLICK! And hereâs the output of this portionâŠ.
Finally, weâd like to easily do conversions between different units, so we define a to() method on Amountâs metaclassâŠ.hereâs some examples of that and CLICK! The output