Let's have this class structure:
abstract class Event {}
class EventImpl extends Event {}
class Foo<T extends Event> {
public void foo(T event) {}
}
class FooImpl extends Foo<EventImpl> {
}
I'm trying to find by reflection the Method foo from a FooImpl instance. I suposed this should work:
FooImpl fooImpl = new FooImpl();
Class clazz = fooImpl.getClass();
Method foo = clazz.getMethod("foo", EventImpl.class);
However, I'm getting a NoSuchMethodException. It seems to be that the generic parameter T, fixed as EventImpl, cannot be recognized as a method parameter, because if I try to find the method in this way, is working fine:
Method fooParent = clazz.getMethod("foo", Event.class);
Is normal this behaviour or I'm missing something here? Thanks for the help.
Just a basic test to help to reproduce the error:
@Test
public void test() throws Exception {
FooImpl fooImpl = new FooImpl();
// Method foo of class FooImpl need to be called with parameter EventImpl
fooImpl.foo(new EventImpl());
Class clazz = fooImpl.getClass();
Method fooParent = clazz.getMethod("foo", Event.class); // OK
Method foo = clazz.getMethod("foo", EventImpl.class); // NoSuchMethodException
}
This is due to the type erasure of generics.
From the Oracle Docs:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Since Reflection is a runtime phenomenon, due to type erasure of generics, foo's parameter is replaced to Event (Since it's bound by T extends Event).
Hence, you have to pass the Event.class as the argument.
The keyword here is: bridge methods.
When compiled, the FooImpl will not directly expose a method foo with a parameter of type Event, but it might rather generate an additional (bridge) method (as part of the type erasure process) to ensure that subtyping works as expected:
public void foo(Event event) {
foo((EventImpl) event);
}
private void foo(EventImpl eventImpl) {
}
At Runtime, when your Reflection snipped will be executed and after type erasure has already taken place, the T type-parameter would have already been successfully replaced with Event, but when invoked, the bridge method will help delegates to the original foo method that uses an EventImpl parameter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With