class List { E x; E get() { return x; } void add(E x) { this.x = x; } } public class WildcardExample { void m() { List xs = new List(); // LEGAL: get() returns a ? extends Number. // ? extends Number <: Number. Number n = xs.get(); // ILLEGAL: cannot add _any_ subclass of Number to a list of ? extends Number. // Even though Integer <: Number, Integer !<: ? extends Number. // xs.add(new Integer(3)); // ILLEGAL: this _could_ be allowed since we're just adding back what we // got, but the compiler can't prove that xs in the call to get() is the same // as xs in the call to add(). // xs.add(xs.get()); // ILLEGAL: this is the similar to the previous but it's more clear that // it should be illegal: // class B { // List xs = new List(); // void m() { // xs.add(xs.get()); // // Suppose there is a data race between m() and p() // // Here is possible interleaving with p(): // // // List xs1 = xs; // read a List // // {concurrent call to p()} // // List xs2 = xs; // read a List // // ? extends Number x = xs2.get(); // get an Integer // // xs1.add(x); // add to a List // } // // void p() { // xs = new List(); // } // } // LEGAL: // This last case is legal. By giving a name (T) to the ? type, we can // enforce that the type of the list we're getting from and the type we're // adding to are the same. Note that we had to change the type to allow // the call to add type-check. class C { void m(List xs) { xs.add(xs.get()); } }; new C().m(xs); } }