Details for exercise 10
Closures as annotation values
Using closures as annotation values can be very powerful, for example to express constraints. To acheive it, Groovy leverages Class
values in annotations. For example, an AST transformation defined like this:
@java.lang.annotation.Documented
@Retention(RetentionPolicy.SOURCE)
@Target([ ElementType.METHOD, ElementType.TYPE])
@GroovyASTTransformationClass(["org.codehaus.groovy.transform.ConditionalInterruptibleASTTransformation"])
public @interface ConditionalInterrupt {
...
/**
* Condition should be set as a closure expression.
* @return
*/
Class value();
}
Then the fact of defining value
of type Class
make the AST transformation accept ClosureExpression
as an argument.
Tips and tricks
-
As we use the
Class
type, one could be tempted to use your AST transformation using a class literal as an argument, so you should always check that the type of the expression that you receive is aClosureExpression
, not aClassExpression
for example…
Comments
Would it be easy to make it compatible with @CompileStatic? Why?
No, it’s not easy. There are multiple reasons for that, but the primary reason here is that you have a single closure expression that will be used for multiple parameters which can have distinct types. So even if you transform the closure expression so that it
becomes an explicit type, you would face the problem that all arguments would need to have the same type. So the only solution for you is to clone
the ClosureExpression
, and by clone
we mean a deep cloning. Unfortunately, Groovy doesn’t provide such a facility, so it’s very hard to do. The alternative is to use distinct closure expressions for each argument.
Can you make this annotation work with separate closures for each argument?
Yes, you can change the annotation to have each parameter annotated with a distinct closure. However, it is not as easy as it seems, because if you use the AST transformation annotation itself, you will face a problem: the annotated note will be of type Parameter
and there’s no way to find the enclosing method or the enclosing class node, so you would not be able to actually update the body! The solution is to keep the AST transformation at the method level, and use a distinct annotation to provide the closure expressions.