Monday, August 09, 2004

Trickiness with generics and reflection for methods

This caught me offguard at first:

public class Scratch { public static class Qux { } public static class SubQuux extends Qux { } public static class Foo <M extends Qux> { public void spam(final M meat) { } } public static class Bar extends Foo<SubQuux> { public void spam(final SubQuux meat) { } } public static void main(final String[] args) { try { for (final Method method : Bar.class.getMethods()) if ("spam".equals(method.getName())) System.out.println(method); } catch (Exception e) { e.printStackTrace(); } } }

So, just what does main print? The answer is fascinating:

public void Scratch$Bar.spam(Scratch$SubQuux) public volatile void Scratch$Bar.spam(Scratch$Qux)

Aha! Now I get it. Since the JDK5 compiler implements generics with type erasure, there needs to be a version of spam which takes the lowest possibly erased type, Qux, so that JDK1.4 runtimes can handle the method call. Crud. That messes up my reflection for messaging. However, the solution is staring right at me: volitile. Since there really is no such method as public void Scratch$Bar.spam(Scratch$Qux), the compiler synthesizes one and marks it as volitile. Nifty. Now I can filter those out with Modifier.isVolitile. No harm, no foul.

No comments: