我遇到了一个示例,该示例表明在方法签名和方法上擦除操作有所不同,但我不知道为什么/如何做。 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 中编译而不会发出警告。

09-30 15:04
查看更多