我想写一个像这样的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),另一个方法返回实现类的迭代器(供内部使用)。
答案 0 :(得分:4)
答案 1 :(得分:3)
如果Maple
是Tree
和Plant
,因为它扩展了两者,那么您想要使用的超级子句中的重点是什么?您可以通过经典子类型多态性将Mapple
分配给Object
,分配给Tree
或Plant
个引用。
? 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.
}
这消除了创建包装器对象的需要,但代价是所有类型安全。