凝聚“超级”

时间:2012-05-03 20:34:24

标签: java generics

我想写一个像这样的Interator:

class Plant { }
class Tree extends Plant { }
class Maple extends Tree { }

// Iterator class: compiler error on the word "super".
class MyIterator<T super Maple> implements Iterator<T> {
    private int index = 0;
    private List<Maple> list = // Get the list from an external source.

    public T next() {
         Maple maple = list.get(index++);
         // Do some processing.
         return maple;
    }

    // The other methods of Iterator are easy to implement.
}

从概念上讲,这个想法是让迭代器看起来像返回树或植物(即使它们总是Maples),而不为每个迭代器编写单独的类。

但是当我用T super Maple生成时,编译器不喜欢它;显然,您只能使用T extends Something生成一个类。有谁知道完成同样事情的好方法?

我的询问动机是我有一个程序,它的API使用接口。我想有一个方法返回接口的迭代器(对于API),另一个方法返回实现类的迭代器(供内部使用)。

5 个答案:

答案 0 :(得分:4)

答案 1 :(得分:3)

如果MapleTreePlant,因为它扩展了两者,那么您想要使用的超级子句中的重点是什么?您可以通过经典子类型多态性将Mapple分配给Object,分配给TreePlant个引用。

? extends T? super T都是通配符,声明为替换类型参数T的类型参数。

您打算做的是定义类型参数的边界,而不是类型参数。你可以简单地将你的类型参数声明为T,没有边界,然后,当你使用它时,你使用通配符作为类型参数。

class MyIterator<T> implements Iterator<T> { ... }

当你使用它时:

MyIterator<? super Maple> iter;

答案 2 :(得分:1)

好的,所以你的迭代器会通过maple返回一个迭代器,但你想把它作为一个iteratorr转换为Plant。

让我们假装(暂时)Iterator有一个方法insertInto(T t),它将一个对象放在迭代器所在的列表中。 。如果将迭代器转换为Iterator&lt; Plant&gt;然后这意味着可以调用i.insertInto(myCarrot) - 因为胡萝卜是植物。但这会违反这些类型 - 它会试图将胡萝卜放入基础的枫树列表中。

换句话说,你的迭代器是而不是一个植物的迭代器,它不会只需要任何旧的植物作为它的方法参数,这就是为什么你不能将它作为这样的方法来转换或生成它

答案 3 :(得分:0)

不允许声明super - 有界类型参数,the reasoning is that it is not useful

你已经设法偶然发现一个有趣的案例,其中 有意义,因为如果你实现了一个只读迭代器,它可以方便绑定它。

简单的选择就是让它实现Iterator<Maple>,或Iterator<T>进行一些类型检查。

答案 4 :(得分:0)

显然没有理想的方法,所以我建议两个次优解决方案。

第一个是这样的包装器迭代器:

public class SuperTypeIterator<E> implements Iterator<E> {
    private final Iterator<? extends E> iter;

    public SuperTypeIterator(Iterator<? extends E> iter) {
        this.iter = iter;
    }

    @Override
    public E next() {
        return iter.next();
    }

    // And similarly for the other methods of Iterator
}

这允许您更改返回类型,如下所示:

Iterator<Plant> getPlantIterator() {
    return new SuperTypeIterator<Plant>( new MapleIterator() );
}

这提供了完整的类型安全性,但却以创建新对象为代价。

另一种方法是使用未选中的强制转换:

Iterator<Plant> getPlantIterator() {
    return (Iterator<Plant>) (Iterator<?>) new MapleIterator();
    // Yes, the double cast is necessary to get it to compile.
}

这消除了创建包装器对象的需要,但代价是所有类型安全。