Blazing Fast Reflection - For Java
By accident my main feed, rather than my Java tag feed, was aggregated to Javablogs. This led quite a few Java developers to my post on reflection optimization for .NET. To mend the damage done, I wrote a small sample that uses Hibernate's reflection optimizer to optimize reflection in Java. The design of Hibernate is quite similar to NHibernate, so this was fairly easy to do. The Clonable (it's misspelled in the API) interface in Java is horribly broken, so I didn't do the entire clone thing. Below is an example on how to create a reflection optimizer for a bean.
ArrayList<String> getters = new ArrayList<String>();
ArrayList<String> setters = new ArrayList<String>();
ArrayList<Class> types = new ArrayList<Class>();
Method[] allMethods = Customer.class.getMethods();
for (Method method : allMethods)
{
if (method.getName().startsWith("get"))
{
getters.add(method.getName());
types.add(method.getReturnType());
try
{
Method setterMethod = Customer.class.getMethod("set" + method.getName().substring(3), method.getReturnType());
setters.add(setterMethod.getName());
}
catch (NoSuchMethodException e)
{
setters.add(null);
}
}
}
BytecodeProviderImpl bytecodeProvider = new BytecodeProviderImpl();
ReflectionOptimizer optimizer = bytecodeProvider.getReflectionOptimizer(
Customer.class,
getters.toArray(new String[0]),
setters.toArray(new String[0]),
types.toArray(new Class[0])
);
You can then use the AccessOptimizer to get or set property values, or the InstantiationOptimizer to create new class instances in a similar fashion to what I showed in the .NET post.
Object[] values = optimizer.getAccessOptimizer().getPropertyValues(customer1);
optimizer.getAccessOptimizer().setPropertyValues(customer2, values);
The above snippet shows a basic example of how you can do a shallow clone of any object using Hibernate's reflection optimizer.
The key reason for writing the .NET sample was to show the massive performance improvements optimized reflection has over plain old reflection. So how does Java stack up?
On my laptop, running JUnit 4 inside of IntelliJ IDEA one the Java 1.6 JRE (pew!) it took 21578 milliseconds to assign the values of one instance to another one million times. Using Hibernate's reflection optimizer it took only 79 milliseconds. So we see the same kind of performance improvement in Java as we did on .NET. My biggest surprise was that Java was way faster than .NET; pure reflection on .NET took almost a minute, while light-weight code generation optimized reflection took ~179 milliseconds. To be honest, I expected it to be the other way around.