Java 泛型与继承角情况
我有以下代码,它的行为与我所想的不一样。我已经把这些评论放在了一起,说明了正在发生的事情和人们的期望Java 泛型与继承角情况,java,generics,Java,Generics,我有以下代码,它的行为与我所想的不一样。我已经把这些评论放在了一起,说明了正在发生的事情和人们的期望 class C<T> { void m(T arg) { } } interface I { void m(Class arg); } class D extends C<Class<String>> implements I { } // expected : error -- conflicting inherited meth
class C<T> {
void m(T arg) {
}
}
interface I {
void m(Class arg);
}
class D extends C<Class<String>> implements I {
}
// expected : error -- conflicting inherited methods
// actual: error -- abstract method not overridden
abstract class E extends C<Class<String>> implements I {
}
// expected : error -- conflicting inherited methods
// actual: no error, but no bridge method
C类{
无效m(T参数){
}
}
接口I{
无效m(arg类);
}
类D扩展了C实现I{
}
//应为:错误--继承的方法冲突
//实际值:错误--未重写抽象方法
抽象类E扩展了C实现I{
}
//应为:错误--继承的方法冲突
//实际:无错误,但无桥接方法
有人能帮我理解这个行为吗。
D
没有继承I
的任何内容,因为它没有默认方法,只有一个方法签名,而您确实没有为其提供实现。正如@RC指出的,您的m
的类型差异很大,因此不认为它是有效的实现。要获得关于冲突的错误,实际上需要添加冲突的实现
在
E
中,您将类标记为抽象类,因此可以不按照接口中的要求实现方法。只要在扩展了E
的任何类中执行此操作,就会触发关于冲突实现的错误,因为在类型擦除之后,类型将是相同的。基本上,您只是将问题从D
转移到将要扩展的E
我希望在jdk8
中进行以下讨论
在您的代码中:
class D extends C<Class<String>> implements I {
让我们看看为什么应该在接口I
中实现该方法。根据JLS8.4.8.1
:
An instance method mC declared in or inherited by class C, overrides from C another
method mI declared in an interface I, iff all of the following are true:
• I is a superinterface of C.
• mI is an abstract or default method.
• The signature of mC is a subsignature (§8.4.2) of the signature of mI.
在上述代码中,mC
是void m(Class arg)
在ClassD
中,mI
是void m(Class arg)
并参见子签名的规范:
And the `JLS8.4.2` explain the subsignature between two method:
The signature of a method m1 is a subsignature of the signature of a method m2 if
either:
• m2 has the same signature as m1, or
• the signature of m1 is the same as the erasure (§4.6) of the signature of m2.
根据上述规范,void m(Class arg
不是void m(Class arg)
的子签名,但是void m(Class arg)
是void m(Class arg
的子签名。因此类D
应该显式地实现接口I
请参阅以下代码来解释它:
class C {
public void m(Class arg) {
}
}
interface I<T> {
void m(T arg);
}
class D extends C implements I<Class<String>> {
}
根据上述规范,带有
T
的Class
参数的void m(T arg)
方法将转换为void m(Class arg)
。转换后的方法将被擦除为void m(Class arg)
,此方法将存在于类D
中,因此类D
不会继承类C
中的方法void m(t arg)
我尝试一步一步地解释发生了什么:
类D
扩展了C
,它引入了带有签名void m(类arg)的方法
在类C
定义后的编译时。但是由于类型擦除,编译后类型参数字符串的信息将被删除。因此字节码将创建一个由类C
定义的方法void m(class arg)
此外,类D
实现接口I
,该接口要求D
实现方法m(类arg)
,该方法在编译时不同于方法m(类arg)
由于类型参数,因此必须为前一个方法提供实现或声明D
abstract
如果您为接口I
声明的方法m(Class arg)
提供实现,那么实际上您将重写ClassC
定义的方法,因为在应用类型擦除后,两者将具有相同的签名
如果你让classD
extendC
,事情就会改变。在这种情况下,classC
将引入一个在编译时带有签名void m(Collection arg)
的方法,该方法在发生类型擦除后变为void m(Collection args)
,作为字节码。但是现在该方法void m(class arg)
由接口I
声明的方法具有不同的签名,因此不会重写而是重载类C
定义的方法m
下面是一个简单的Java示例:
class C<T> {
void m(T arg) {
System.out.println("Method [m] of class [C] called");
}
}
class D extends C<Class<String>> implements I {
@Override
public void m(Class arg) {
System.out.println("Method [m] of class [D] called");
}
}
class DD extends C<Collection<String>> implements I {
@Override
public void m(Class arg) {
System.out.println("Method [m] of class [DD] called");
}
}
问题可以归结为Java泛型的一个基本事实,即将实例化泛型类型分配给原始类型是安全的,但反之则不正确。重写方法时,不允许不安全的转换。因此,Class
无法重写原始类类型
在这种情况下:
Class a = String.class; //type safe
Class<String> b = a; //not type safe
Class a=String.Class;//类型安全
类b=a;//不是类型安全的
井类与类的类型不同解决方案可能是在II
中添加一个类型,并在I中添加m(类
)
class C<T> {
void m(T arg) {
System.out.println("Method [m] of class [C] called");
}
}
class D extends C<Class<String>> implements I {
@Override
public void m(Class arg) {
System.out.println("Method [m] of class [D] called");
}
}
class DD extends C<Collection<String>> implements I {
@Override
public void m(Class arg) {
System.out.println("Method [m] of class [DD] called");
}
}
public static void main(String[] args) {
new D().m(D.class);
new D().m((Class<String>) null);
new DD().m(DD.class);
new DD().m(new ArrayList<String>());
}
Method [m] of class [D] called
Method [m] of class [D] called
Method [m] of class [DD] called
Method [m] of class [C] called.
Class a = String.class; //type safe
Class<String> b = a; //not type safe