简体   繁体   中英

Generic types in Java - advanced polymorphism

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?

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> 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);
    }
}

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> List<? super ElementA> means a List of some type which is ElementA or a superclass.

List<? extends ElementB> 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM