6

作为该主题中一般问题的一个实际示例,我想在接口中实现该containsAll方法Set

public boolean containsAll(Iterable<?> c) { /* ... */ }

我认为这应该是允许的,因为CollectionIterable意味着这containsAll将涵盖接口要求。同样,更一般地说,能够使用参数超类实现接口似乎应该可以工作。

但是,Eclipse 说没有办法(还没有直接尝试过 javac)——有人能解释一下原因吗?我确信规范中有一些东西使它成为这样,但我也想了解需求的动机。还是我错过了像Iterable<?>不是超类这样的东西Collection<?>

作为一个附带问题 - 鉴于我声明了两种方法,带有签名的方法在带有参数Iterable的调用中总是首选吗?Collection

日食错误:

如果我删除带有Collection签名的方法,只留下Iterable一个(见错误后),我会得到以下信息:

The type BitPowerSet must implement the inherited abstract method Set<Long>.containsAll(Collection<?>)

确切的实现是:

@Override public boolean containsAll(Collection<?> c) {
  for (Object o : c) if (!contains(o)) return false;
  return true;
}
public boolean containsAll(Iterable<?> c) {
  for (Object o : c) if (!contains(o)) return false;
  return true;
}
4

3 回答 3

5

由于您正在实现的接口声明了(抽象)方法containsAll(Collection<?>),因此您必须使用这个确切的签名来实现它。Java 不允许您实现/覆盖具有比原始参数类型更广泛的参数类型的方法。Collection这就是为什么当您使用签名注释掉您的方法时会出现错误的原因。

当方法没有被注释掉时,你没有显示你声称得到的其他错误,但我想它可能不得不对模棱两可的方法重载做一些事情。

于 2010-10-16T16:21:16.537 回答
2

我对java为什么有这个限制的猜测是,假设你有:

class A {
    void foo(String s) { ...  }
}

class B extends A {
    // Note generalized type
    @Override void foo(Object s) { ...  }
}

现在,如果您有class C extends B并且它想要覆盖foo,则不清楚它应该采用什么参数。

比如说 C 首先直接扩展 A,覆盖void foo(String s),然后将其更改为扩展 B。在这种情况下,C 现有的覆盖foo将变得无效,因为 Bfoo应该能够处理所有Objects,而不仅仅是Strings。

于 2010-10-16T16:34:44.820 回答
0

参数类型是方法签名的一部分,因此 jvm 需要一个具有完全相同签名的方法来查找覆盖。containsAll(Iterable) 将具有与 containsAll(Collection) 不同的签名。

如果我没记错的话,编译器必须使用一些变通方法来使泛型工作,尽管有这个限制。

对于第二个问题,编译器更喜欢 Collection 参数,因为它是 Iterable 的子类型,这使得 Collection 方法比 Iterable 方法更具体。

于 2010-10-16T16:55:30.507 回答