【问题标题】:Why do I get the base class methods via reflection when the subclass overrides them?当子类覆盖它们时,为什么我会通过反射获得基类方法?
【发布时间】:2011-11-10 09:11:42
【问题描述】:

我有超类:

class MyClass<T> {
  public void setValue(T value){
    //insert code
  }

  public T getValue(){
    return null;
  }
}

那我有一个具体的推导

class MyClassImp extends MyClass<String> {
  @Override
  public void setValue(String value){
    //insert code
  }

  @Override
  public String getValue(){
    return null;
  }
}

关于MyClassImpl 的反思:

Class clazz = MyClassImpl.class;
Method[] methods = clazz.getDeclaredMethods();

我得到了超类实现java.lang.Object getValue()void setValue(java.lang.Object)java.lang.String getValue()void setValue(java.lang.String)

根据Class.getDeclaredMethods()vis-a-viz的Java文档

返回一个 Method 对象数组,反映由该 Class 对象表示的类或接口声明的所有方法。这包括公共、受保护、默认(包)访问和私有方法,但不包括继承的方法。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口未声明任何方法,或者此 Class 对象表示基本类型、数组类或 void,则此方法返回长度为 0 的数组。返回的数组中不包含类初始化方法&lt;clinit&gt;。如果该类声明了多个具有相同参数类型的公共成员方法,则它们都包含在返回的数组中。

为什么我得到超类型实现? 有什么我遗漏的吗?

我需要这个的原因是我在基类实现上反射性地调用setValue,我已经添加了一些特殊的注释cmets,当然还有额外的约束。

【问题讨论】:

标签: java generics inheritance reflection


【解决方案1】:

这是因为编译后的类实际上确实声明了setValue(Object)。该方法将转换为 String,然后调用强类型方法。同样getValue(Object) 调用getValue(String)

基本上这是必需的,因为 JVM 并不真正了解泛型(至少不是深入了解) - 为了在 JVM 级别覆盖超类方法,它必须具有相同的签名。

看看javap -c MyclassImp 的类,你会看到额外的合成方法:

public java.lang.Object getValue();
  Code:
   0:   aload_0
   1:   invokevirtual   #3; //Method getValue:()Ljava/lang/String;
   4:   areturn

public void setValue(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   checkcast       #4; //class java/lang/String
   5:   invokevirtual   #5; //Method setValue:(Ljava/lang/String;)V
   8:   return

}

【讨论】:

  • 请注意,Method.isSynthetic() 将返回 true 用于那些生成/返回 Object 的方法。这是区分这些方法和“真实”方法的好方法。
  • 此编译器行为是否记录在某处?
  • @Ryan:我不知道,恐怕。简单地看一下 JLS,我看不到它——尽管在一些地方提到了这种情况。
  • @Joh,我想知道为什么文档说对方法 Class.getDeclaredMethods() 的调用将排除继承的方法。
  • @Santosh:它排除了继承但未覆盖的方法。在这种情况下,两种方法都被覆盖。我同意它可以更清楚。
【解决方案2】:

正如 Jon 之前所说,泛型在运行时会丢失类型信息

因此,无论何时使用泛型,编译器都会将所有这些“泛型”继承方法放入子类中。对于非泛型代码,情况并非如此。

我刚刚检查过:当我从超类中删除通用相关代码(&lt;T&gt; 部分)时,反射代码在子类中正好给了我两个方法,即使它覆盖了它们。这意味着文档应该对泛型相关的代码有点明确。

【讨论】:

  • 严格来说,这些方法不存在,因为泛型(如您所见)。它们在那里是因为方法使用返回类型协方差(即子类返回比基类型更具体的值)。这最常与泛型结合使用,但也可以单独使用。
  • 是的,我已经验证了所有场景,其中我以通用形式和显式形式覆盖。使用通用形式,我只有两种方法,使用协方差,我注意到另外两种方法,它们被标记为合成
猜你喜欢
  • 2011-07-09
  • 2011-09-16
  • 1970-01-01
  • 2014-11-10
  • 2019-07-11
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 2012-01-31
相关资源
最近更新 更多