繁体   English   中英

通过反射获取具有固定通用参数的父方法

[英]Get parent method with fixed generic parameter by reflection

让我们有这个类结构:

abstract class Event {}
class EventImpl extends Event {}

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

class FooImpl extends Foo<EventImpl> {
}

我试图通过反射从FooImpl实例中找到Method foo 我认为这应该工作:

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

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

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
}

这是由于泛型类型擦除

来自Oracle Docs:

如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。 因此,生成的字节码仅包含普通的类,接口和方法。

由于Reflection是一种运行时现象,由于泛型的类型擦除, foo的参数被替换为Event (因为它受T extends Event约束)。

因此,您必须传递Event.class作为参数。

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

编译时, FooImpl不会直接使用Event类型的参数公开方法foo ,但它可能会生成一个额外的(桥接)方法(作为类型擦除过程的一部分),以确保子类型按预期工作:

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

private void foo(EventImpl eventImpl) {

}

在运行时,当您的Reflection snipped将被执行并且在类型擦除已经发生之后, T类型参数将已经成功替换为Event ,但是在调用时,bridge方法将帮助委托给使用的原始foo方法一个EventImpl参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM