5

想象一下下面的代码:

class A {}
class B extends A {}

interface IA extends Iterable<A> {}
interface IB extends Iterable<B> {}

理想情况下,我希望接口IB也能够扩展IA,因为它实际上允许您检索As。

interface IB extends Iterable<B>, IA {}

甚至

interface IB extends Iterable<B> implements IA {}

但是,编译器真的不喜欢这两个,如果允许这样做,它将使我的代码更好,因为从概念上讲 B 可以用作 A 而无需到处向上转换

我有哪些解决方案可以解决这个问题?

4

3 回答 3

9

泛型的非协变意味着你想要的东西是不可行的(至少在一般情况下不可行)。

但是,也许通配符可以解决您的特定问题?例如

void methodThatOperatesOnA(Iterable<? extends A> it) {
    ...
}

it这将允许您从 s中提取元素A,但编译器会阻止您插入对象,*因为它不能保证保持不变量。


* 除了null 和其他一些古怪的情况

于 2013-01-18T20:41:50.763 回答
2

一个类型不能有两个超类型G<X> and G<Y> where X!=Y- 可能是由于擦除。

解决问题的一种方法是对要迭代的类型使用类型参数

interface IA<X extends A> extends Iterable<X>
{
    @Override Iterator<X> iterator();
}
interface IB extends IA<B>
{
    @Override Iterator<B> iterator();
}

我通常会避免这种情况,它只是更复杂。


在您的示例中,首先扩展Iterable<A>可能不是一个好主意。例如

class Team implements Iterable<Member>
{
    public Iterator<Member> iterator() { ... }
}

for(Member member : team) ...

但团队概念可能不仅仅是成员的集合。可能更清楚

class Team
{
    Iterable<Member> members() { ... }
}
for(Member member : team.members()) ...

在这种情况下,您的类型可以设计为

interface IA
{
    Iterable<? extends A> members();
}

interface IB extends IA
{
    @Override
    Iterable<? extends B> members();
}

人们也可以质疑是否Iterable应该更放松,而不是

public interface Iterable<T> 

    Iterator<T> iterator();

会不会更好

public interface Iterable<T> 

    Iterator<? extends T> iterator();
于 2013-01-18T21:16:04.443 回答
1

Java 中的泛型不是协变的——也就是说,List<String> instanceof List<Object>是假的。因此,我相信,IB不是编译器问题的一个实例,因此也是导致编译器问题的原因。IA

想象一个新的类,class C extends A {}. C IS-A A,因此 C 将是 IA 包含的合法值——但不是 IB。因此,如果您尝试将 IB 用作 IA 的实例,则可以插入 C 并破坏类型安全保证。

于 2013-01-18T20:36:36.040 回答