【发布时间】:2017-07-28 01:21:18
【问题描述】:
在实现大量使用泛型的 Java 接口时,我发现了一个奇怪的不一致之处,我无法解释为什么会发生这种情况。
我已经尽可能地精简了这个例子,而且我知道在这个例子中使用泛型已经没有多大意义了。
public interface Service<T> {
public List<T> thisCompiles();
public List<T> andThisCompiles(List<Object> inputParam);
public <S extends T> List<S> thisCompilesAswell();
public <S extends T> List<S> evenThisCompiles(List inputParam);
public <S extends T> List<S> butThisDoesnt(List<Object> inputParam);
}
public class ServiceImpl implements Service<Number> {
@Override
public List<Number> thisCompiles() {
return null;
}
@Override
public List<Number> andThisCompiles(List<Object> inputParam) {
return null;
}
@Override
public List<Number> thisCompilesAswell() {
return null;
}
@Override
public List<Number> evenThisCompiles(List inputParam) {
return null;
}
@Override
public List<Number> butThisDoesnt(List<Object> inputParam) {
return null;
}
}
请注意,在实现中,所有返回类型都是List<Number>,虽然接口更慷慨。
在编译这个 sn-p 时(我尝试过 Oracle JDK 8 u144 和 OpenJDK 8 u121),我收到以下错误消息:
-
ServiceImpl类型的方法butThisDoesnt(List<Object>)必须重写或实现超类型方法 - 类型
ServiceImpl必须实现继承的抽象方法Service<Number>.butThisDoesnt(List<Object>) - 名称冲突:
ServiceImpl类型的方法butThisDoesnt(List<Object>)与Service<T>类型的butThisDoesnt(List<Object>)具有相同的擦除,但不会覆盖它
似乎只要我传递给函数的参数本身具有一些通用参数,编译就会失败。
实现第5个函数
@Override
public <S extends Number> List<S> butThisDoesnt(List<Object> inputParam) {
return null;
}
按预期工作。
是否有对这种行为的解释或者我偶然发现了一个错误(尽管它也发生在 OpenJDK 中)?为什么第 5 个函数的行为不同?
我对如何编译这段代码不感兴趣(我为我的代码库修复了它)。我只是偶然发现了这个问题,并对逻辑解释为什么编译器接受前四种方法但拒绝第五种方法感到好奇。
【问题讨论】:
-
<S extends Number> List<S>与List<Number>不同,因此第一个版本不会覆盖抽象方法。所以很明显你还没有实现所有的方法。很明显你实际上并没有覆盖任何东西,所以你的@Override很糟糕。最后,您遇到了名称冲突,因为method(List<A> a)与method(List<B> b)的签名相同,如下面的答案所述。 -
@matt 检查方法 3 和 4(
thisCompilesAswell和evenThisCompiles)。它们还返回List<Number>,同时在接口中定义为<S extends T> List<S>,但它们可以编译。名称冲突只是@Override不起作用的结果。 -
然后删除@Override,我认为您仍然会遇到名称冲突。我真的很惊讶其他方法编译。类型擦除表示它们具有相同的签名。
-
@matt 相同的签名不同的名字?
-
@CarlosHeuberger 什么?他说名称冲突是因为 Override 不起作用。不是,这是因为他有一个方法
butThisDoesnt(List<Object> inputParam),它与接口中的相同方法发生冲突。不是实际的@注释。