我有超级课程:
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
返回一个Method对象数组,这些对象反映由此Class对象表示的类或接口声明的所有方法。这包括公共,受保护,默认(包)访问和私有方法,但不包括继承的方法。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口未声明任何方法,或者此Class对象表示基本类型,数组类或void,则此方法返回长度为0的数组。类初始化方法
<clinit>
未包含在返回的数组中。如果类声明具有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。
为什么我会获得超级类型的实现? 有什么我想念的吗?
我需要这个的原因是我在基类实现上反射性地调用了setValue
,我添加了一些特殊的注释注释,当然还有其他约束。
答案 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>
部分)时,反射代码在子类中给了我两个方法,即使它覆盖了它们。这意味着文档应该对通用相关代码有点明确。