Java 当子类重写基类方法时,为什么要通过反射获得它们?

Java 当子类重写基类方法时,为什么要通过反射获得它们?,java,generics,inheritance,reflection,Java,Generics,Inheritance,Reflection,我的超级班级是: class MyClass<T> { public void setValue(T value){ //insert code } public T getValue(){ return null; } } 我得到了超类实现java.lang.Object getValue(),void setValue(java.lang.Object)和java.lang.String getValue(),void setValue(jav

我的超级班级是:

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

  public T getValue(){
    return null;
  }
}
我得到了超类实现
java.lang.Object getValue()
void setValue(java.lang.Object)
java.lang.String getValue()
void setValue(java.lang.String)

根据
Class.getDeclaredMethods()的Java文档

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

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


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

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

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

使用
javap-cmyclassmp
查看类,您将看到额外的合成方法:

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

}

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

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


我刚刚检查了一下:当我从超类中删除了泛型相关代码(“代码”)部分时,反射代码在子类中正好给出了两个方法,尽管它覆盖了它们。这意味着文档对于泛型相关的代码应该有一点明确。

可能是@RC的副本:我看不出它怎么可能是这个问题的副本。@RC这不是一个完全的副本注意
Method.isSynthetic()
将返回
true
,用于那些生成的方法,这些方法接受/返回
对象
。这是区分那些方法和“真实”方法的一个好方法。编译器的这种行为在什么地方有记录吗?@Ryan:恐怕我不知道。简单地查看一下JLS,我看不到它-尽管在一些地方提到过这种情况。@Joh,我想知道为什么文档中说调用method Class.getDeclaredMethods()将排除继承的方法。@Santosh:它排除继承但未重写的方法。在这种情况下,两个方法都被重写。我同意它可能更清晰。严格地说,这些方法不存在是因为泛型(正如您所注意到的)。它们之所以存在,是因为方法使用返回类型协方差(即子类返回比基类型更具体的值)。这通常与泛型结合使用,但可以单独使用。是的,我已经验证了所有场景,其中我以泛型形式和显式形式重写。对于泛型形式,我只有两种方法,对于协方差,我注意到另外两种方法,它们被标记为合成
Class clazz = MyClassImpl.class;
Method[] methods = clazz.getDeclaredMethods();
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

}