当子类覆盖它们时,为什么我通过反射获取基类方法?

时间:2011-09-05 05:53:55

标签: java generics inheritance reflection

我有超级课程:

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的数组。类初始化方法<clinit>未包含在返回的数组中。如果类声明具有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。

为什么我会获得超级类型的实现? 有什么我想念的吗?

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

2 个答案:

答案 0 :(得分:14)

这是因为编译后的类实际上 声明了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

}

答案 1 :(得分:1)

正如Jon先前所说,类型信息在运行时遗失了泛型

因此,无论何时使用泛型,编译器都会将所有这些“通用”继承方法放在子类中。非通用代码也是如此。

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