How can I obtain the type parameter of a generic i

2020-03-19 04:21发布

问题:

I have this interface:

public interface EventHandler<T extends Event> {
    void handle(T event);
}

And this class implementing it:

public class MyEventHandler implements EventHandler<MyEvent> {
    @Override
    public void handle(MyEvent event) {
        //do something
    }
}

In this clase, T parameter is MyEvent, that is a concrete implementation of Event. How can obtain this using reflection?

回答1:

Resolve the type of T by the generic interface. E.g.

public interface SomeInterface<T> {
}

public class SomeImplementation implements SomeInterface<String> {

    public Class getGenericInterfaceType(){
        Class clazz = getClass();
        ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericInterfaces()[0];
        Type[] typeArguments = parameterizedType.getActualTypeArguments();
        Class<?> typeArgument = (Class<?>) typeArguments[0];
        return typeArgument;
    }
}

public static void main(String[] args) {
    SomeImplementation someImplementation = new SomeImplementation();
    System.out.println(someImplementation.getGenericInterfaceType());
}

PS: Keep in mind that the acutalTypeArguments are of type Type. They must not be a Class. In your case it is a Class because your type definition is EventHandler<MyEvent>.



回答2:

When trying to do anything non-trivial with generics and reflection, consider Guava's TypeToken:

private interface Event {}

private interface EventHandler<T extends Event> {
    void handle(T event);
}

TypeToken<?> findEventType(final Class<? extends EventHandler> handlerClass) throws Exception {
    final TypeToken<? extends EventHandler> handlerTypeToken = TypeToken.of(handlerClass);
    final Invokable<? extends EventHandler,Object> method = handlerTypeToken.method(EventHandler.class.getDeclaredMethod("handle", Event.class));
    return method.getParameters().get(0).getType();
}

public void testExploreGuavaTypeTokens() throws Exception {
    class MyEvent implements Event {}

    class MyEventHandler implements EventHandler<MyEvent> {
        @Override public void handle(final MyEvent event) {}
    }

    assertEqual(MyEvent.class, findEventType(MyEventHandler.class).getRawType());
}

(Note that the TypeToken returned by findEventType() could contain much richer type information than a Class can represent; that's why it's the caller's decision whether to simplify it via getRawType().)