the 'read-only' objects in generics type with wildcards

712 Views Asked by At

Consider the following code (the two questions are inside the code):

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 ?
    }
}
  1. Why does b2.addChildren(new Tree<Number>()) not work?
  2. But it works with diamond <> b2.addChildren(new Tree<>()). Which type list the compiler uses inside the diamond <>?
1

There are 1 best solutions below

2
On

The problem is that you have declared b2 to have a type of Tree<?>.

The problem is clearer if you rewrite your main method as two methods:

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 ?
}

Even though you created a new Tree<Number>(), you are immediately discarding that information. Your code is only remembering that b2 contains some unknown type of Tree, since Tree<?> means "some type of Tree but I don't know what type."

Since we don't know what the Tree's type is, how do we know if it's safe to call addChildren(new Tree<Number>()), or new Tree<Integer>() or new Tree<String>() or new Tree<JTable>()? The compiler has no idea. You may remember what you put in there, but the type of b2 doesn't carry that information, so the compiler doesn't have any way to know.