我遇到了一个示例,该示例表明在方法签名和方法上擦除操作有所不同,但我不知道为什么/如何做。 JLS §8.4.8.3指出:
编译时错误example given:
class C<T> {
T id (T x) {...}
}
class D extends C<String> {
Object id(Object x) {...}
}
说明:
前两点很明显,但我不理解解释的后两点。如果第三点成立,两种方法如何具有相同的擦除?从第三点来看,似乎使用参数化类C 的方法(即id(String)而不是id(T))擦除签名。如果是这种情况,则这两种方法应具有不同的擦除,但是该示例表明该方法擦除是在非参数化类上完成的。那么,擦除实际上是如何应用于方法签名和方法的呢?
最佳答案
擦除意味着所有出现的通用类型T
(在C情况下为String
)都替换为Object
(+必需的类型转换)。由于类型信息以这种方式丢失,因此示例中存在冲突-JVM无法确定要调用的方法。
编辑(这是错误的):
afaik :子签名是一种接受兼容类型(例如String的父类(super class)型)和/或返回协变类型的方法。
我尝试了一下,这很令人困惑,但得出以下解释:编译器不会擦除,而是替换通用签名。因此,当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 中编译而不会发出警告。