Wednesday, December 10, 2008

Reflection with Generics

I ran into some pretty interesting reflection problem today, and thought to jog down some notes here. In Java Generic, concept introduced with Java 5, is implemented pretty much as a compiler trick which performs the type check then generates the same byte code as the non-generic code, and inserts casts for the generic code automatically. The following two code samples will generate identical byte code by the compiler.


void doSomething(List ids)


vs.


void doSomething(List<Long> ids)


* That's also why code like: list instanceof List<Long> does not work in Java since this information is not available at runtime

So now lets imagine you try to use the reflection to find the method with a List<Double> at runtime, you will actually be able to find the method doSomething(List<Long> ids) and you can even invoke the method without any problem since at the byte code level this method is really taking a non-generic List. Now the problem arise, in the method since you already specify the generic parameter thus you probably will not check the element of the list again but rather use foreach loop as shown in the following code sample:


for(Long id : ids){
...
}


This code works fine without reflection since the compiler will force the type check, but while working with reflection you can basically pass in any List at runtime, hence you will get a class cast exception at the start of this loop. Fortunately the generic type information is not all gone at the runtime. They do get compiled into meta information for the class so using reflection you can retrieve this kind of information at the runtime, although I have to say I am not a big fan how this generic related type API was designed in the reflection package, its very inconvenient to use to say the best. If you are interested IBM Developer Works has an excellent article on this topic.

In summary, keep in mind generic is just a compile time transformation and be very careful when using reflection with generic.

No comments: