Eliminate Boilerplate Code with the PICA Technique for Java : Page 3

Combining reflection, dynamic proxies, and annotations proves to be such a powerful technique that it deserves its own name.

by Paul Holser

Aug 17, 2009

Page 3 of 3

Further Exploration

I encourage you to take a long look at SchemaValidator and the work that it does. As you might imagine, it makes heavy use of Reflection to discover characteristics of the methods on the PICA and of the PICA annotations.

Also look at the declarations of the annotation interfaces. Here's BoundProperty in a nutshell:

For the PICA technique to work, the annotation interfaces must be marked with the meta-annotation @Retention with a value of RetentionPolicy.RUNTIME. Otherwise, the annotations will not be available in the class files at runtime. Each of these interfaces is marked also with the meta-annotation @Target, with a value of ElementType.METHOD, to indicate that the annotations can be applied only to methods. Some PICA implementations use annotations that can be applied to other program elements, such as types and method parameters.

Recall that an InvocationHandler for a dynamic proxy is called upon to respond not just to the methods on the interfaces the proxy implements, but also the java.lang.Object methods equals(), hashCode(), and toString(). Therefore, the PropertyBinderInvocationHandler's invoke() method first checks to see whether the method it was asked to handle was declared on java.lang.Object; if so, it then attempts to respond sensibly to equals() (uses identity-based equality on the proxy), hashCode() (gives the result of System.identityHashCode() on the proxy), or toString() (gives the name of the PICA interface plus the toString() value of all the bound properties).

You might be wondering how this utility is able to support PICA methods whose return type is a generic List, e.g. List<Integer>. Aren't generic types erased at compile time? They are for instances—at runtime I cannot reflect on a List instance and know that its compile time type was List<String>. However, if you reflect on a Method whose return type is a generic type (or whose parameters are generic types), you can discover the properties of those declarations using Method.getGenericReturnType() and Method.getGenericParameterTypes(). These return instances of objects that implement Type. When the type is a generic type, the Type instances will be ParameterizedTypes. You can then ask the ParameterizedTypes object what its type arguments represent—specific classes, type variables, bounds, etc. Look at ListValueConverter to see how it uses these Reflection capabilities to deduce the element type of the Lists it is asked to create.

I hope you have as much fun digging into the details of the PICA technique as I did. Perhaps you will find novel ways to use PICA to eliminate repetitive, boilerplate imperative code in your project.

References:

Paul Holser is a Principal Consultant at Improving Enterprises, where he and his colleagues help clients adopt agile software development methods and create indefinitely sustainable software applications. He has seventeen years of software development experience with thought leaders such as ThoughtWorks, ObjectSpace, and Valtech. He is the author of JOpt Simple, a command-line option parsing library for Java.