反射

2016-01-05 52 views
3
获取父类的方法固定泛型参数

让我们有这样的阶级结构:反射

abstract class Event {} 
class EventImpl extends Event {} 

class Foo<T extends Event> { 
    public void foo(T event) {} 
} 

class FooImpl extends Foo<EventImpl> { 
} 

我试图通过从FooImpl实例反射Methodfoo找到。我suposed这应该工作:

FooImpl fooImpl = new FooImpl(); 
Class clazz = fooImpl.getClass(); 
Method foo = clazz.getMethod("foo", EventImpl.class); 

但是,我得到一个NoSuchMethodException。这似乎是泛型参数T,固定为EventImpl,不能被识别为方法的参数,因为如果我试图找到这样的方法,工作正常:

Method fooParent = clazz.getMethod("foo", Event.class); 

是正常的这种行为或我在这里错过了什么? 感谢您的帮助。

只是一个基本的测试,以帮助重现错误:

@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 
} 
+1

您没有重写类“FooImpl”中的方法。通过这样做,你的测试会通过。 – Seelenvirtuose

+0

感谢您的评论。其实这是我的临时解决方案,但我认为这可能是其他选择。 – troig

回答

2

这是由于仿制药type erasure

从Oracle文档:

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.

由于Reflection是一个运行时的现象,由于输入泛型的擦除,foo的参数被替换到Event(因为它是由T extends Event结合的)。

因此,您必须通过Event.class作为参数。

+0

谢谢你的回答。这似乎是我需要回顾一下关于类型擦除的一些基本概念:)到目前为止我已经清楚 – troig

1

这里的关键字是:桥接方法

编译时,FooImpl不会直接暴露的方法fooEvent类型的参数,但它可能更产生额外的(桥)方法(如类型擦除过程的一部分),以确保亚型作品预期:

public void foo(Event event) { 
    foo((EventImpl) event); 
} 

private void foo(EventImpl eventImpl) { 

} 

在运行时,当你的倒影剪断,将被执行,后类型擦除已经发生,该T型参数会已经成功地与Event取代,但在被调用时,电桥法将帮助代表改为使用EventImpl参数的原始foo方法。

+0

感谢您的建议 – troig