4

This code:

interface I {}

public class Test {

    TableView<? extends I> table;

    Test(ObservableList<? extends I> list) {
        table = new TableView<>();
        table.setItems(list);
    }
}

...produces this message:

error: method setItems in class TableView<S> cannot be applied to given types;
       table.setItems(list);
 required: ObservableList<CAP#1>
 found: ObservableList<CAP#2>
 reason: argument mismatch; ObservableList<CAP#2> cannot be converted to ObservableList<CAP#1>
 where S is a type-variable:
   S extends Object declared in class TableView
 where CAP#1,CAP#2 are fresh type-variables:
   CAP#1 extends I from capture of ? extends I
   CAP#2 extends I from capture of ? extends I

How can there be a type mismatch between two identical types? What am I missing?

rgettman
  • 176,041
  • 30
  • 275
  • 357
chrox
  • 43
  • 1
  • 3
  • 2
    Yesterday I had some fruit. i.e. `? extends Fruit` Today I had some fruit. `? extends Fruit`. Can someone assume from that information, I had the same type of fruit both days? No. `some fruit #1` might not be `some fruit #2` – Peter Lawrey Feb 25 '14 at 19:33

3 Answers3

6

In Java Generics, ? wildcards cannot be identical, because they represent unknown types. An unknown type cannot equal some other unknown type.

If you want the types to match, then you must define a generic type parameter on the Test class, and refer to it throughout your class.

public class Test<T extends I> {

    TableView<T> table;

    Test(ObservableList<T> list) {
        table = new TableView<>();
        table.setItems(list);
    }
}
rgettman
  • 176,041
  • 30
  • 275
  • 357
2
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

"Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

On the other hand, given a List, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected." (http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html)

Smutje
  • 17,733
  • 4
  • 24
  • 41
1

Don't use "? extends I" in your variable declaration. It adds no semantic value and is the source of your issue. You see the compiler is refusing to assume that the '?' in one declaration is identical to the '?' in another. This is natural since there is no guarantee that they are. I would dispense with the '?' altogether:

interface I {}

public class Test {

    TableView<I> table;

    Test(ObservableList<? extends I> list) {
        table = new TableView<>();
        table.setItems(list);
    }
}
AlphaGeek
  • 383
  • 2
  • 11
  • 1
    Even better: don't use generics unless you are writing some really generic components to be distributed and allowed to be extended (by the open/closed principle). – robermann Feb 25 '14 at 19:36