我遇到了一个示例,表明在方法签名和方法上擦除的方式不同,但我不知道为什么/如何。 JLS §8.4.8.3州:
如果类型声明T具有成员方法m1并且存在以T形式声明的方法m2或T的超类型以使得满足以下所有条件,那么这是编译时错误:
- m1和m2具有相同的名称。
- m2可从T。
访问- m1的签名不是m2签名的子签名(§8.4.2)。
- m1的签名或某些方法m1覆盖(直接或间接)具有与m2的签名相同的擦除或某种方法m2覆盖(直接或间接)。
醇>
编译时错误example given:
class C<T> {
T id (T x) {...}
}
class D extends C<String> {
Object id(Object x) {...}
}
解释:
这是非法的,因为D.id(Object)是D的成员,C.id(String)以D的超类型声明,并且:
- 这两个方法具有相同的名称,id
- C.id(String)可供D
访问- D.id(Object)的签名不是C.id(String)
的签名- 这两种方法具有相同的擦除
前两点很明显,但我不明白解释的最后两点。如果第三点成立,这两种方法如何具有相同的擦除?从第三点来看,似乎使用参数化类C&lt; String&gt;的方法(即id(String)而不是id(T))完成签名的擦除。如果是这种情况,那么这两种方法应该有不同的擦除,但是该示例表明方法擦除是在非参数化类上完成的。那么,如何在方法签名和方法上实际应用擦除?
答案 0 :(得分:2)
擦除意味着所有通用类型T
(在C情况下为String
)的出现都被Object
(+所需类型转换)替换。由于类型信息以这种方式丢失,因此示例中存在冲突 - JVM无法决定调用哪种方法。
编辑(这是错误的): afaik :子签名是接受兼容类型(例如超类型String)和/或返回协变类型的方法。
我尝试了它并且令人困惑,但是得出了这样的解释:编译器不会擦除,而是替换通用签名。因此,当D extends C<String>
覆盖C<String>.id
的签名变为:String id(String x)
时。显然,D
的方法id
具有不同的签名,也没有相同的删除(因为String
不是通用类型)。因此,D.id
的签名不是C
的子签名。 (符合规则3)
另一方面,C<T>.id(T x)
的删除为Object id(Object x)
,与D.id(Object x)
的删除相同。 (符合规则4)
在此之后,如果可以保持签名和删除对齐,则覆盖id
是有效的。显然这是可能的(虽然不是很有用):
class Foo<T> {
public void set(final T thing) {}
}
class Bar<T> extends Foo<T> {
@Override
public void set(final Object thing) {}
}
这在没有警告的情况下编译eclipse。