带有通配符的泛型类型中的"只读"对象

Sim*_*imo 2 java generics wildcard readonly diamond-operator

考虑以下代码(代码中有两个问题):

import java.util.*;

public class Tree<T> {
    private T info;
    private List<Tree<? extends T>> children = new ArrayList<Tree<? extends T>>();

    public Tree<? extends T> getChildren(int n) {
        return children.get(n);
    }

    public void addChildren(Tree<? extends T> children) {
        this.children.add(children);
    }

    public static void main(String[] args) {
        Tree<?> b2; // so b2 is a reference of a Tree of unknown type
        b2 = new Tree<Number>(); /* to allow b2 to call addChildren() with Tree<? extends Number> aguments */
        b2.addChildren(new Tree<Number>()); // 1) why it doesn't work ?
        b2.addChildren(new Tree<Integer>()); // neither does this one!
        b2.addChildren(new Tree<>()); // 2) but with diamond <> it works ?
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 为什么不起作用b2.addChildren(new Tree<Number>())
  2. 但它适用于钻石<> b2.addChildren(new Tree<>()).编译器在菱形<>中使用哪种类型列表?

VGR*_*VGR 5

问题是你已声明b2有一种类型Tree<?>.

如果您将main方法重写为两种方法,问题会更清晰:

public static void main(String[] args) {
    test(new Tree<Number>());
}

private static void test(Tree<?> b2) {
    // "<?>" means we don't know what the generic type of b2 is, so
    // the compiler can't possibly know if it's safe to add any type
    // of children...

    b2.addChildren(new Tree<Number>()); // 1) why it doesn't work ?
    b2.addChildren(new Tree<Integer>()); // neither does this one!
    b2.addChildren(new Tree<>()); // 2) but with diamond <> it works ?
}
Run Code Online (Sandbox Code Playgroud)

即使您创建了一个new Tree<Number>(),您也会立即丢弃该信息.你的代码只记得b2包含一些未知类型的树,因为它Tree<?>意味着"某种类型的树,但我不知道什么类型."

由于我们不知道树的类型是什么,我们如何知道它是否可以安全地呼叫addChildren(new Tree<Number>()),new Tree<Integer>()或者new Tree<String>()或者new Tree<JTable>()?编译器不知道.你可能还记得你放在那里的东西,但是那种类型b2不带有那些信息,所以编译器没有任何方法可以知道.