Lists with wildcards cause Generic voodoo error

大憨熊 提交于 2019-11-26 20:20:41
Paŭlo Ebermann

(I assume here that Bar and Baz are both subtypes of Foo.)

List<? extends Foo> means a list of elements of some type, which is a subtype of Foo, but we don't know which type. Examples of such lists would be a ArrayList<Foo>, a LinkedList<Bar> and a ArrayList<Baz>.

As we don't know which subtype is the type parameter, we can't put Foo objects into it, neither Bar or Baz objects. But we still know that the type parameter is a subtype of Foo, so every element already in the list (and which we can get from the list) must be a Foo object, so we can use Foo f = list.get(0); and similar things.

Such a list can only be used for taking elements out of the list, not to adding elements at all (apart from null, but I don't know if the compiler actually allows this).

A List<Foo> on the other hand allows adding any object which is a Foo object - and as Bar and Baz are subtypes of Foo, all Bar and Baz objects are Foo objects, so they can be added, too.

Michael Myers

Remember PECS: Producer Extends, Consumer Super.

Since you are trying to add items to list2, it is a consumer and cannot be declared as List<? extends Foo>. But then you are using list2 as a producer also when you add it to list1. Therefore, list2 is both a producer and a consumer and must be a List<Foo>.

list1, as a pure consumer, can be a List<? super Foo>.

Vivien Barousse

They're errors. Lets modify your code, considering that Bar and Baz are two different types extending Foo:

List<? extends Foo> list1 = new ArrayList<Bar>();
List<? extends Foo> list2 = new ArrayList<Baz>();

If list1.add(new Foo()) was allowed, you could add Foo instances in a collection containing Bar instances. This explains the first error.

If list1.addAll(list2) was allowed, all instances of Baz in list2 would be added to list1, which contains only Bar instances. This explains the second error.

Let me try to explain in what case you might need to use <? extend Classname>.

So, lets say you have 2 classes:

class Grand {
    private String name;

    public Grand(String name) {
        this.setName(name);
    }

    public Grand() {
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Dad extends Grand {
    public Dad(String name) {
        this.setName(name);
    }

    public Dad() {
    }
}

And lets say you have 2 collections, each contains some Grands and some Dads:

    List<Dad> dads = new ArrayList<>();
    dads.add(new Dad("Dad 1"));
    dads.add(new Dad("Dad 2"));
    dads.add(new Dad("Dad 3"));


    List<Dad> grands = new ArrayList<>();
    dads.add(new Dad("Grandpa 1"));
    dads.add(new Dad("Grandpa 2"));
    dads.add(new Dad("Grandpa 3"));

Now, lets asume that we want to have collection, which will contain Grand or Dad objects:

        List<Grand> resultList;
        resultList = dads; // Error - Incompatable types List<Grand> List<Dad>
        resultList = grands;//Works fine

How we can avoid this? Simply use wildcard:

List<? extends Grand> resultList;
resultList = dads; // Works fine
resultList = grands;//Works fine

Notice, that you cant add new items in such (resultList) collection. For more information you can read about Wildcard and PECS conseption in Java

Sorry, maybe I misunderstood your question, but suposing:

public class Bar extends Foo{ }

this code:

List<Foo> list2 = new ArrayList<Foo>()
list2.add( new Bar() );

do not generate any error for me.

So, removing the wild card allows adding subclasses of Foo.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!