Lists with wildcards cause Generic voodoo error

后端 未结 5 627
梦如初夏
梦如初夏 2020-11-27 17:05

Does anyone know why the following code does not compile? Neither add() nor addAll() works as expected. Removing the \"? extends\" part makes everything work, but then I wou

相关标签:
5条回答
  • 2020-11-27 17:22

    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>.

    0 讨论(0)
  • 2020-11-27 17:32

    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.

    0 讨论(0)
  • 2020-11-27 17:37

    (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.

    0 讨论(0)
  • 2020-11-27 17:40

    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

    0 讨论(0)
  • 2020-11-27 17:45

    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.

    0 讨论(0)
提交回复
热议问题