views:

118

answers:

4

In Java, assume I have the following class Container that contains a list of class Items:


    public class Container<T>
    {
        private List<Item<? extends T>> items;

        private T value;

        public Container(T value)
        {
            this.value = value;
        }

        public void addItem(Item<? extends T> item)
        {
            items.add(item);
        }

        public void doActions()
        {
            for (Item<? extends T> item : items)
            {
                item.doAction(value);
            }
        }
    }

    public abstract class Item<T>
    {
        public abstract void doAction(T item);
    }

Eclipse gives the error: The method doAction(capture#1-of ? extends T) in the type Item is not applicable for the arguments (T)

I've been reading generics examples and various postings around, but I still can't figure out why this isn't allowed. Eclipse also doesn't give any helpful tips in its proposed fix, either. The variable value is of type T, why wouldn't it be applicable for ? extends T?.

A: 

See my (only!) blog post. It doesn't directly talk about the "? extends T" case, but everywhere you see "?", just imagine it says "? extends Object" and it should become clear.

Basically, you've got a list of "something that extends T", but you don't know what that something is at compile time.

dty
+5  A: 

Take a look at the following program

public class Cell<T> { 
  private T value;

  public void set(T t) { value = t; }
  public T get() { return value; }
}


Cell<Integer> ci = new Cell<Integer>();
Cell<? extends Number> cn = ci;

cn.set(new Double(5.0));  // (A) <-- Compile error here


Integer n = ci.get(); // (B) Runtime error!  

As you said, the (A) line does not compile. If this line were legal then, at runtime, the program will pass a Double object to the cn.set() call where cn's dynamic type is Cell<Integer>.

When execution subsequently arrives at (B), ci.get() will return a Double object---the one that was passed in (A)---were the declaration of ci says that its get() method is guaranteed to return an Integer. In order to prevent this conundrum (which actually breaks the JVM's strong typing philosophy), the compiler disallows assignment from T to <? extends T>.

Itay
This makes sense. I hadn't considered such an intermediary assignment.
Matt
+1  A: 

PECS - Producer-extends, consumer-super

replace all occurrences of extends with super in your declarations (including the one you omitted, in the loop), and it compiles :)

Bozho
I actually tried that, but it doesn't get me quite what I wanted. However, it may be the best I can hope for.
Matt
what it didn't give you?
Bozho
I actually wanted to be able to add an Item<S> to the Container where S is a subclass of T. With super I can add, for example, an Item<Object>.
Matt
A: 

The bounding is reversed. Item<X> can take an X or a subclass of X for its doAction. Now ? extends T means that X is a subclass of T. So you're passing in a T into an item that expects an X, which is a subclass of T, not the other way around.

Yuliy