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
}