Better reflection using proxy classes
Setting up correctly reflections can be a pain, using proxies classes can make this task both safer and easier.
Using proxy classes with reflection
Reflection in java is a neat feature that allows one work with classes loaded at runtime with little knowledge of the actual source, maybe the API only.
The problem is that the whole process is cubersome and time consuming, and makes your code unreadable beyond belief. The best soluction I found so far is the usage on another neat feature of the java language, proxy classes.
The idea is pretty simple, you create an interface with all the methods you want to run on the class you’re loading at runtime. A helper class then gets this interface as a skeleton and returns an implementation of the same interface that simply forwards the calls to the desired class.
By adding annotations to the mix, the loaded interface might even have parameters from types that you’re reflecting also.
All the caveats of using reflections continue to apply as usual. Reflection defeats the compiler’s type checking, making all the checks in runtime, deffering checks to a later stage is bad. Reflections probably defeat a lot of optimizations and might be considerable slower than an actual class. But unfortunatelly there are times where you cannot scape them and in those cases I believe that the readability bennefits of this approach gains on the extra overhead.
Java proxy
Java proxies work by using an implementation of the interface "InvocationHandler". This interface has one method that receives all calls made to the proxy instance, as arguments it receives the proxy instance it self, the method that is being invoked and also the arguments. The method should return the value that is to be returned to the parent.
To bootstrap a proxy object all you need is one or more "Classes" objects that your proxy instance will "implement" and your InvocationHandler. The Proxy static method "getProxyClass" will return the instance that implements the classes that you provided, the implementation will simply call the provided InvocationHandler.
Proxy interfaces
In our original problem we have classes that we know how they are implemented, but don’t have access to the actual implementations during compile time. But we know exactly what we want to call on those classes, not only that we also know the correct signature.
As I said before we will use a mock interface that has all the methods that we are interested in calling. Our proxy object will implement this interface, use the Method instance passed to the InvokerHandler to discover the real method that we’re interested on the real object and call this. That is similar to the old ways of using reflection to execute the call, but instead of hard coding all the calls we use a java Interface as blueprint to the reflection.
This makes both the specification of the reflection and the usage much more readable.
Unknown classes
Since we’re loading classes that are not available during the compile time, there’s a possibility that one of the methods receive or return an unknown to us class. The easiest way to solve this is to create an annotation and annotate the specification interface accordingly.
Example
class UseUnknowClass {
static private interface Uknown {
@Actual("com.notme.UnknownTwo") Object getUnknown2();
void doSomething(@Actual("com.notme.UknownTwo") Object param);
}
Unknown myUnknownInstance;
public UseUnknowClass(Object unknown) {
myUnknownInstance = ReflectionHelper.getProxyFor("com.notme.Uknown", unknown, Unknown.class);
myUnknownInstance.doSomething(myUnknownInstance.getUnknown2());
}
}