Diamond in Generics Java 1.7 - 如何在1.6中为Java编译器编写这个

时间:2011-12-07 01:09:08

标签: java generics

如何为不能使用钻石的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的元素中读取每个元素。

5 个答案:

答案 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;
}

我认为这样的改变不会破坏现有代码,因为类型约束与现有签名相同。