7

我想写一个这样的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.
}

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

T super Maple但是当我用;进行泛化时,编译器不喜欢它。显然你只能使用T extends Something. 有谁知道完成同样事情的好方法?

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

4

5 回答 5

4

请参阅:为什么类型参数没有下限?

于 2012-05-03T20:55:44.110 回答
3

如果Maple是 aTree和 a Plant,因为它同时扩展了两者,那么在您要使用的超级子句中又有什么意义呢?您可以通过经典的子类型多态性分配MappleObjectTreePlant引用。

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

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

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

当你使用它时:

MyIterator<? super Maple> iter;
于 2012-05-03T20:50:31.437 回答
1

好的,所以你的迭代器返回了一个基于 maple 的迭代器,但是你想将它转换为一个基于 Plant 的迭代器。

让我们假设(片刻)Iterator 有一个方法 insertInto(T t) 将一个对象放入列表中迭代器所在的位置。. 如果将迭代器转换为 Iterator<Plant> 则意味着可以调用 i.insertInto(myCarrot) - 因为胡萝卜是植物。但这会违反类型 - 它会试图将胡萝卜放入枫树的基础列表中。

换句话说,您的迭代器不是Plants 上的迭代器,它不会将任何旧植物作为其方法参数,这就是为什么您不能这样转换或生成它的原因。

于 2012-05-04T02:59:22.893 回答
0

它声明一个super-bounded 类型参数是不允许的,原因是它没有用

您成功地偶然发现了一个有趣的案例,嗯,它确实有意义,因为如果您正在实现一个只读迭代器,那么像这样绑定它很方便。

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

于 2012-05-03T20:57:34.833 回答
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.
}

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

于 2012-05-07T09:54:18.553 回答