我遇到了一个例子,它表明在方法签名和方法上擦除是不同的,但是我不知道为什么/怎么做。JLS§8.4.8.3指出:
如果类型声明T有成员方法m1,并且存在在T中声明的方法m2或T的超类型,则这是一个编译时错误,使得以下所有条件都保持不变:
编译时错误给出实例
class C<T> {
T id (T x) {...}
}
class D extends C<String> {
Object id(Object x) {...}
}解释如下:
这是非法的,因为D.id(Object)是D的成员,C.id(String)是在D的超级类型中声明的,并且:
前两点是显而易见的,但我不明白最后两点的解释。如果第三点成立,这两种方法怎么能有相同的擦除呢?从第三点看,似乎使用参数化类C的方法(即id(String)而不是id(T))删除签名。如果是这样的话,那么这两个方法应该有不同的擦除,但是这个例子表明方法擦除是在非参数化类上完成的。那么,擦除实际上是如何应用于方法签名和方法的呢?
发布于 2013-05-22 13:07:31
擦除意味着泛型类型T (C情况下的String)的所有出现都被Object (+必需的类型转换)所取代。由于类型信息是以这种方式丢失的,因此示例中存在冲突-- JVM将无法决定调用哪种方法。
编辑(这是错误的):afaik:子签名将是接受兼容类型(例如超级字符串类型)和/或返回协变类型的方法。
我尝试了一下,它让人困惑,但我想到了这样的解释:编译器不会擦除,而是替换泛型签名。因此,当D extends C<String>时,overriden 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中编译。
https://stackoverflow.com/questions/16690135
复制相似问题