Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The static type checker is built using Groovy’s existing powerful AST (Abstract Syntax Tree) transformation mechanisms but for those not familiar with these mechanisms you can think of it as an optional compiler plugin triggered through an annotation. Being an optional feature, you are not forced to use it if you don’t need it. To trigger static type checking, just use the @TypeChecked annotation on a method or on a class to turn on checking at your desired level of granularity. Let’s see that in action with a first example:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

void someMethod() {}

@TypeChecked
void test() {
    // compilation error:
    // cannot find matching method sommeeMethod()
    sommeeMethod()

    def name = "Marion"
    // compilation error:
    // the variable naaammme is undeclared
    println naaammme
}

...

The static type checker also verifies that the return types and values of your assignments are coherent:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

@TypeChecked
Date test() {
    // compilation error:
    // cannot assign value of Date
    // to variable of type int
    int object = new Date()

    String[] letters = ['a', 'b', 'c']
 
    // compilation error:
    // cannot assign value of type String
    // to variable of type Date
    Date aDateVariable = letters[0]

    // compilation error:
    // cannot return value of type String
    // on method returning type Date
    return "today"
}

...

Since we’re mentioning type inference, let’s have a look at some other occurrences of it. We mentioned the type checker tracks the return types and values:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked
 
@TypeChecked
int method() {
   if (true) {
       // compilation error:
       // cannot return value of type String
       // on method returning type int
       'String'
   } else {
       42
   }
} 

...

The static type checker, however, won’t complain for certain automatic type conversions that Groovy supports. For instance, for method signatures returning String, boolean or Class, Groovy converts return values to these types automatically:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

@TypeChecked
boolean booleanMethod() {
   "non empty strings are evaluated to true"
}

assert booleanMethod() == true

@TypeChecked
String stringMethod() {
   // StringBuilder converted to String calling toString()
   new StringBuilder() << "non empty string"
}

assert stringMethod() instanceof String

@TypeChecked
Class classMethod() {
   // the java.util.List class will be returned
   "java.util.List"
}

assert classMethod() == List 

The static type checker is also clever enough to do type inference:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

@TypeChecked
void method() {
   def name = "  Guillaume  "
 
   // String type inferred (even inside GString)
   println "NAME = ${name.toUpperCase()}"

   // Groovy GDK method support
   // (GDK operator overloading too)
   println name.trim()

   int[] numbers = [1, 2, 3]
   // Element n is an int
   for (int n in numbers) {
       println n
   }
}

...

An important aspect to have in mind is that using the static type checking facility restricts what you are allowed to use in Groovy. Most runtime dynamic features are not allowed, as they can’t be statically type checked at compilation time. So adding a new method at runtime through the type’s metaclasses is not allowed. But when you need to use some particular dynamic feature, like Groovy’s builders, you can opt out of static type checking should you wish to.
 
The @TypeChecked annotation can be put at the class level or at the method level. So if you want to have a whole class type checked, put the annotation on the class, and if you want only a few methods type checked, put the annotation on just those methods. Also, if you want to have everything type checked, except a specific method, you can annotate the latter with @TypeChecked(TypeCheckingMode.SKIP) — or @TypeChecked(SKIP) for short, if you statically import the associated enum. Let’s illustrate the situation with the following script, where the greeting() method is type checked, whereas the generateMarkup() method is not:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked
import groovy.xml.MarkupBuilder

// this method and its code are type checked
@TypeChecked
String greeting(String name) {
   generateMarkup(name.toUpperCase())
}

// this method isn't type checked
// and you can use dynamic features like the markup builder
String generateMarkup(String name) {
   def sw = new StringWriter()
   new MarkupBuilder(sw).html {
       body {
           div name
       }
   }
   sw.toString()
}

assert greeting("Cédric").contains("<div>CÉDRIC</div>")

...

Current production releases of Java don’t support general type inference; hence we find today many places where code is often quite verbose and cluttered with boilerplate constructs. This obscures the intent of the code and without the support of powerful IDEs is also harder to write. This is the case with instanceof checks: You often check the class of a value with instanceof inside an if condition, and afterwards in the if block, you must still use casts to be able to use methods of the value at hand. In plain Groovy, as well as in the new static type checking mode, you can completely get rid of those casts.

Code Block
languagegroovy
 
import groovy.transform.TypeChecked
import groovy.xml.MarkupBuilder

@TypeChecked
String test(Object val) {
   if (val instanceof String) {
       // unlike Java:
       // return ((String)val).toUpperCase()
       val.toUpperCase()
   } else if (val instanceof Number) {
       // unlike Java:
       // return ((Number)val).intValue().multiply(2)
       val.intValue() * 2
   }
}

assert test('abc') == 'ABC'
assert test(123)   == '246'

...

The static type checker goes a bit further in terms of type inference in the sense that it has a more granular understanding of the type of your objects. Consider the following code:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

// inferred return type:
// a list of numbers which are comparable and serializable
@TypeChecked test() {
   // an integer and a BigDecimal
   return [1234, 3.14]
} 

...

Although this is not really recommended as a good practice, sometimes developers use the same untyped variable to store values of different types. Look at this method body:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

@TypeChecked test() {
   def var = 123             // inferred type is int
   var = "123"               // assign var with a String

   println var.toInteger()   // no problem, no need to cast

   var = 123
   println var.toUpperCase() // error, var is int!
} 

The var variable is initialized with an int. Then, a String is assigned. The “flow typing” algorithm follows the flow of assignment and understands that the variable now holds a String, so the static type checker will be happy with the toInteger() method added by Groovy on top of String. Next, a number is put back in the var variable, but then, when calling toUpperCase(), the type checker will throw a compilation error, as there’s no toUpperCase() method on Integer.
 
There are some special cases for the flow typing algorithm when a variable is shared with a closure which are interesting. What happens when a local variable is referenced in a closure inside a method where that variable is defined? Let’s have a look at this example:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

@TypeChecked test() {
   def var = "abc"
      
   def cl = {
       if (new Random().nextBoolean()) var = new Date()
   }
   cl()
   var.toUpperCase() // compilation error!
} 

The var local variable is assigned a String, but then, var might be assigned a Date if some random value is true. Typically, it’s only at runtime that we really know if the condition in the if statement of the closure is made or not. Hence, at compile-time, there’s no chance the compiler can know if var now contains a String or a Date. That’s why the compiler will actually complain about the toUpperCase() call, as it is not able to infer that the variable contains a String or not. This example is certainly a bit contrived, but there are some more interesting cases:

Code Block
languagegroovy
 
import groovy.transform.TypeChecked

class A           { void foo() {} }
class B extends A { void bar() {} }

@TypeChecked test() {
   def var = new A()
   def cl = { var = new B() }
   cl()
   // var is at least an instance of A
   // so we are allowed to call method foo()
   var.foo()
} 

...

As we shall see in the following chapter about the JDK 7 alignments, Groovy 2.0 supports the new “invoke dynamic” instruction of the JVM and its related APIs, facilitating the development of dynamic languages on the Java platform and bringing some additional performance to Groovy’s dynamic calls. However, unfortunately shall I say, JDK 7 is not widely deployed in production at the time of this writing, so not everybody has the chance to run on the latest version. So developers looking for performance improvements would not see much changes in Groovy 2.0, if they aren’t able to run on JDK 7. Luckily, the Groovy development team thought those developers could get interesting performance boost, among other advantages, by allowing type checked code to be compiled statically.
 
Without further ado, let’s dive in and use the new @CompileStatic transform:

Code Block
languagegroovy
 
import groovy.transform.CompileStatic

@CompileStatic
int squarePlusOne(int num) {
   num * num + 1
}

assert squarePlusOne(3) == 10 

...

In Java 6 and before, as well as in Groovy, numbers could be represented in decimal, octal and hexadecimal bases, and with Java 7 and Groovy 2, you can use a binary notation with the “0b” prefix:

Code Block
languagegroovy
 
int x = 0b10101111
assert x == 175

byte aByte = 0b00100001
assert aByte == 33

int anInt = 0b1010000101000101
assert anInt == 41285

...

When writing long literal numbers, it’s harder on the eye to figure out how some numbers are grouped together, for example with groups of thousands, of words, etc. By allowing you to place underscore in number literals, it’s easier to spot those groups:

Code Block
languagegroovy
 
long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010

...

When catching exceptions, we often replicate the catch block for two or more exceptions as we want to handle them in the same way. A workaround is either to factor out the commonalities in its own method, or in a more ugly fashion to have a catch-all approach by catching Exception, or worse, Throwable. With the multi catch block, we’re able to define several exceptions to be catch and treated by the same catch block:

Code Block
languagegroovy
 
try {
   /* ... */
} catch(IOException | NullPointerException e) {
   /* one block to handle 2 exceptions */
}

...

Code Block
languagehtml/xml
 
...
<taskdef name="groovyc"
        classname="org.codehaus.groovy.ant.Groovyc"
        classpathref="cp"/>
...
<groovyc srcdir="${srcDir}" destdir="${destDir}" indy="true">
   <classpath>
...
   </classpath>
</groovyc>
... 

The Groovy Eclipse Maven compiler plugin hasn’t yet been updated with the support of Groovy 2.0 but this will be the case shortly. For GMaven plugin users, although it’s possible to configure the plugin to use Groovy 2.0 already, there’s currently no flag to enable the invoke dynamic support. Again, GMaven will also be updated soon in that regard.
 
When integrating Groovy in your Java applications, with GroovyShell, for example, you can also enable the invoke dynamic support by passing a CompilerConfiguration instance to the GroovyShell constructor on which you access and set the optimization options:

Code Block
languagejava
 
CompilerConfiguration config = new CompilerConfiguration();
config.getOptimizationOptions().put("indy", true);
config.getOptimizationOptions().put("int", false);
GroovyShell shell = new GroovyShell(config); 

...

To add new methods to an existing type, you’ll have to create a helper class that will contain those methods. Inside that helper class, all the extension methods will actually be public (the default for Groovy but required if implementing in Java) and static (although they will be available on instances of that class). They will always take a first parameter which is actually the instance on which this method will be called. And then following parameters will be the parameters passed when calling the method. This is the same convention use for Groovy categories.
 
Say we want to add a greets() method on String, that would greet the name of the person passed in parameters, so that you could that method as follow:

Code Block
languagegroovy
 
assert "Guillaume".greets("Paul") == "Hi Paul, I'm Guillaume" 

To accomplish that, you will create a helper class with an extension method like so:

...

Static extension methods are defined using the same mechanism, but have to be declared in a separate class. The extension module descriptor then determines whether the class provides instance or static methods. Let’s add a new static method to Random to get a random integer between two values, you could proceed as in this class:

Code Block
languagegroovy
 
package com.acme

class MyStaticExtension {
   static String between(Random selfType, int start, int end) {
       new Random().nextInt(end - start + 1) + start
   }
} 

That way, you are able to use that extension method as follows:

Code Block
languagegroovy
 
Random.between(3, 4)

Extension module descriptor

Once you’ve coded your helper classes (in Groovy or even in Java) containing the extension methods, you need to create a descriptor for your module. You must create a file called org.codehaus.groovy.runtime.ExtensionModule in the META-INF/services directory of your module archive. Four essential fields can be defined, to tell the Groovy runtime about the name and version of your module, as well as to point at your helper classes for extension methods with a comma-separated list of class names. Here is what our final module descriptor looks like:

Code Block
languagegroovy
 
moduleName = MyExtension
moduleVersion = 1.0
extensionClasses = com.acme.MyExtension
staticExtensionClasses = com.acme.MyStaticExtension

...