2

I have 3 simple classes as follows:

public class ElementA {}
public class ElementB extends ElementA {}
public class ElementC extends ElementB {}

Then if I want to create, for example, generic List which takes only subclasses of ElementA class I can declare it as:

List<? super ElementA> list = new ArrayList<>();

and then use it as follows:

list.add(new ElementA());
list.add(new ElementB());
list.add(new ElementC());

which is fine and can be compiled without errors. But I became confused if I want to store anything but not ElementC or ElementB or ElementA. I declare such List as follows:

List<? extends ElementC> list = new ArrayList<>();

and I can't use it at all because it can store only null values. Same thing happen when I declare List as (notice that I'm using class which is 'in the middle of family'):

List<? extends ElementB>

Why so?

  • 2
    Why would you expect `List extends ElementC> list = new ArrayList<>();` to store anything, you don't have any classes that extend ElementC ? – Chris Oct 04 '13 at 15:21
  • Please read the end of my question. I tried it even with Object class and still that List can store only null values – Tomasz Bielaszewski Oct 04 '13 at 15:22
  • `List extends Object> list = new ArrayList <>();` ? You should initialize your list with an element like so: `List extends ElementB> list = new ArrayList();` – Chris Oct 04 '13 at 15:24
  • 1
    Java 7 does not require this. – Tomasz Bielaszewski Oct 04 '13 at 15:24
  • 2
    [This](http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) pretty much covers it. – GriffeyDog Oct 04 '13 at 15:29
  • you can create collection which store you object of given type, but you cant create collection which store you anything but given type. (unless you create your own collection). so when you created collection `List extends ElementC> list = new ArrayList<>();` it means you can put there anything which inherit ElementC, and of course null as null is special case – user902383 Oct 04 '13 at 15:31
  • you should use extends rather that super in this case. List super ElementA> list = new ArrayList<>(); should be List extends ElementA> list = new ArrayList<>(); – rgasiore Oct 04 '13 at 16:04

3 Answers3

3

The problem is that the value of ? is not known at runtime. You have to substitute a concrete class/interface in order to be able to do what you want.

If you do this:

List<ElementA> list = new ArrayList<ElementA>();

you are fine since ElementB is an ElementA at the same time. Same stands for ElementC.

List<? extends ElementA> makes sense if you for example declare it in a class and in a subclass you can substitute something concrete as the type parameter. Clumsy example:

public class SomeClass<T> {
    private List<? extends T> list;

    public void setList(List<? extends T> list) {
        this.list = list;
    }
}

public class SomeConcreteClass extends SomeClass<Integer> {

    public void doSomething() {
        List<Integer> list = new ArrayList<Integer>();
        setList(list);
    }
}
Adam Arold
  • 29,285
  • 22
  • 112
  • 207
1

List<ElementA> accepts instances of ElementA, ElementB, and Element C.

List<ElementB> accepts instances of ElementB and Element C.

List<ElementC> accepts instances of ElementC.

There is no reason for the wildcard in your examples.

List<? super ElementA> means a List of some type which is ElementA or a superclass.

List<? extends ElementB> means a List of some type which is a subclass of ElementB. If you get an element it will be ElementB or a subclass, but it doesn't know what the class is, so it can't be sure the element you add is of the right type, since it is unknown (though it does know it to be a subclass of ElementB).

There are uses for wildcard, but your example is not one of them.

Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • Regarding `List super ElementA>` take a look at [here](http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html#What%20is%20a%20wildcard%20instantiation?): "The wildcard parameterized type `Comparator super String>` is the family of all instantiations of the `Comparator` interface for type argument types that are **supertypes** of `String`". You seem to be stating the opposite. – rsenna Oct 04 '13 at 15:31
  • "`List extends ElementB>` means a List of some type which is a subclass of `ElementB`" -- Actually, it can be a List of `ElementB` as well; it's not restricted to _subclasses_ of `ElementB`. – Ted Hopp Oct 04 '13 at 15:37
  • Good catch. Typo on my part. Fixed. – Paul Draper Oct 04 '13 at 15:38
0

You create a List like this

List<? extends ElementC> list = new ArrayList<>();

but let's say, because it's still valid that you got the List like this

List<? extends ElementC> list = getElementCSubclassList(); // declared as returning a `List<ElementCSubclass>`

Now the compiler cannot know that your list object contains ElementCSubclass objects, it can only be sure that it contains some type of ElementC. As such, it can't let you use any methods that expect the actual generic type.

Imagine

public class ElementCSubclass1 extends ElementC {}
public class ElementCSubclass2 extends ElementC {}
...
List<? extends ElementC> list = getElementCSubclass1List(); // declared as returning a `List<ElementCSubclass1>`

list.add(new ElementCSubclass2()); // this would immediately have to fail

Compiler does this so that the previous situation never occurs.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724