如何为不能使用钻石的Java 1.6编译器编写Java 1.7代码?
示例:
private ReplacableTree<E> convertToIntended(Tree<? extends E> from,ReplacableTree<E> to) {
TreeIterator<? extends E> it = new TreeIterator<>(from.getRoot());
while(it.hasNext()) {
E e = it.next().getElem();
to.add(e);
}
return to;
}
public class TreeIterator<E> implements TreeIter<Node<E>> {
....
}
不允许写...
TreeIterator<? extends E> it = new TreeIterator<?>(from.getRoot());
TreeIterator<? extends E> it = new TreeIterator<E>(from.getRoot());
TreeIterator<? extends E> it = new TreeIterator<? extends E>(from.getRoot());
特别是第三个对我来说很困惑。为什么不起作用?我只想阅读一个树中的元素(可能是一个子类型树),并在一个新的树中使用类型为E的元素中读取每个元素。
答案 0 :(得分:4)
class instance creation expressions中不允许使用通配符类型作为类型参数:
如果类实例创建表达式中使用的任何类型参数是通配符类型参数(第4.5.1节),则是编译时错误。
所以第一个和第三个变体无效。
变体2无效,因为TreeIterator<E>
构造函数需要Node<E>
,但您只需Node<? extends E>
。
对于解决方案,Java 5和6没有构造函数的类型推断,但确实有方法的类型推断,特别是capture conversion。以下应该编译:
TreeIterator<? extends E> it = makeIterator(from.getRoot());
,其中
private <E> TreeIterator<E> makeIterator(Node<E> node) {
return new TreeIterator<E>(node);
}
修改:您在评论中提问:
TreeIterator
的contstructor参数类型为Node<E>
。因此,Node<E>
的构造函数参数为E
。在编写变体2时,eclipse会说以下内容:构造函数TreeIterator<E>(Node<capture#2-of ? extends E> )
未定义这是什么意思?
作为通配符类型,类型Node<? extends E>
代表一系列类型。 Node<capture#2-of ? extends E>
是指该家庭中的特定类型。在这种情况下,这种区别是无关紧要的。重要的是,Node<? extends E>
不是Node<E>
的子类型,因此您无法将Node<? extends E>
的实例传递给期望Node<E>
的构造函数。
答案 1 :(得分:1)
简而言之,您不会为Java 6编译器编写Java 7代码 - 您必须使用旧的,重复的非菱形语法。不,你不能用源1.7指定1.6的目标,它将无法工作!
答案 2 :(得分:1)
梅里顿已经很好地解释了。我只是想建议你不用没有通配符声明就可以做到:
TreeIterator<E> it = new TreeIterator<E>(from.getRoot());
答案 3 :(得分:1)
通常,<>
表示只使用与左侧声明中相同的类型参数。但在这种情况下,该声明是一个通配符。
使用通配符类型参数new TreeIterator<? extends E>(...)
创建构造函数没有意义,因为通常,如果您不关心要使用的参数,您应该选择满足该范围的任何类型;可以是E
,或任何子类型。
但是,在这种情况下,这不起作用,因为TreeIterator<E>
的构造函数接受类型参数为<E>
的对象。你没有显示TreeIterator
的源代码,所以我无法看到它的作用,但很可能它的界限太严格了。可能会重构它以使类型参数<? extends E>
。
但在某些情况下,这是不可能的。在这种情况下,您仍然可以通过&#34;捕获助手&#34;来消除对类型参数E
的需要。 (上面有什么建议)将带参数E
的东西变成带有通配符? extends E
的东西。
答案 4 :(得分:0)
我知道这是一个老问题,但万一有人偶然发现这个问题,我会想到最明显的写作方式就是:
private <U extends E> ReplaceableTree<E> convertToIntended(Tree<U> from, ReplaceableTree<E> to)
{
TreeIterator<U> it = new TreeIterator<U>(from.getRoot());
while(it.hasNext())
{
E e = it.next().getElem();
to.add(e);
}
return to;
}
我认为这样的改变不会破坏现有代码,因为类型约束与现有签名相同。