34

I have classes A, B, C and D where B extends A, C extends A and D extends A.

I have the following ArrayLists each with a few elements in them:

ArrayList<B> b;
ArrayList<? extends A> mix = b;

I intended for the variable mix to contain elements of type B, C or D. I tried to add an element of type C into mix like this:

mix.add(anElementOfTypeC);

But the IDE doesn't allow me to do so and it says:

anElementOfTypeC cannot be converted to CAP#1 by method of invocation conversion where CAP#1 is a fresh type-variable: CAP#1 extends A from capture of ? extends A

Did I use the <? extends A> correctly? How can I resolve this?

ApproachingDarknessFish
  • 14,133
  • 7
  • 40
  • 79
vda8888
  • 689
  • 1
  • 8
  • 19
  • 3
    From your code extract, `mix` is actually an `ArrayList` here... – fge Jan 13 '13 at 17:32
  • During your research - if you did some, did you came through this SO question - http://stackoverflow.com/questions/1910892/what-is-the-difference-between-super-and-extends-in-java-generics ? – Rohit Jain Jan 13 '13 at 17:36

6 Answers6

45

ArrayList<? extends A> means an ArrayList of some unknown type that extends A.
That type might not be C, so you can't add a C to the ArrayList.

In fact, since you don't know what the ArrayList is supposed to contain, you can't add anything to the ArrayList.

If you want an ArrayList that can hold any class that inherits A, use a ArrayList<A>.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 4
    can't add anything is a bit too strict. We can add `null`, or we can add `list.get(0)` back to the list. I know I'm picking too much. ;) – Rohit Jain Jan 13 '13 at 17:37
  • 1
    And yet you can declare `Class extends A> c = B.class`... Generics give me a real headache :/ – fge Jan 13 '13 at 17:42
  • 3
    @RohitJain: no, you cannot add `list.get(0)` back to the list – newacct Jan 13 '13 at 20:49
  • 2
    @JBNizet: I know what you're talking about; but it's not what the topic of the comment is talking about. That's `List`, not `List extends A>`, which is the topic of the comment. – newacct Dec 16 '13 at 09:13
  • @newacct Re-read the code. The method appends an element from a `List extends A>` to this list. mix is of type `List extends A>`. – JB Nizet Dec 16 '13 at 10:02
  • 1
    @JBNizet: I know what the code says. I am not disputing the fact that the element came from a `List extends A>`. But at the point where you are "adding `list.get(0)`", it is a `List`. That's the difference. – newacct Dec 16 '13 at 10:12
12

It is not possible to add elements in collection that uses ? extends.

ArrayList<? extends A> means that this is an ArrayList of type (exactly one type) that extends A. So you can be sure, that when you call get method, you'll get something that is A. But you can't add something because you don't know what exactly the ArrayList contains.

dev
  • 1,648
  • 16
  • 25
Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
7

Did I use the <? extends A> correctly?

List<? extends A> list; means that the 'list' refers to an object implemented interface List, and the list can hold elements inherited from class A (including A). In other words the following statements are correct:

List<? extends A> listA1 = new ArrayList<A>();
List<? extends A> listB1 = new ArrayList<B>();
List<? extends A> listC1 = new ArrayList<C>();
List<? extends A> listD1 = new ArrayList<D>();

Java generics are a compile-time strong type checking. The compiler uses generics to make sure that your code doesn't add the wrong objects into a collection.

You tried to add C in ArrayList<B>

listB1.add(new C());

If it were allowed the object ArrayList<B> would contain different object types (B,C).
That is why the reference listB1 works in 'read-only' mode. Additionally you could take a look at this answer.

how to resolve this?

List<A> list = new ArrayList<A>();
Community
  • 1
  • 1
Oleks
  • 1,011
  • 1
  • 14
  • 25
3

You could just declare:

ArrayList<A> mix = new ArrayList<A>();

You can add any element of class A or any of its subclasses into such a list. It is just that when you get from that list, you will only be able to call methods available on A.

Note that A is not limited to being a "full fledged" class: it can be an abstract class or even an interface.

fge
  • 119,121
  • 33
  • 254
  • 329
0

You can create the array list of super class type. so you can do this.

ArrayList<A> arrayList=new ArrayList<A>();
sumit sharma
  • 1,067
  • 8
  • 24
0

Look at this example and choose as per your requirement

package com.generic;

import java.util.ArrayList;

public class SuperExtendTest {

    /**
     * @param args
     */
    public static void main(String[] args) {

    }
    public void test() throws Exception {
        ArrayList<Parent> parents=new ArrayList<>();
        parents.add(new Parent());
        parents.add(new Child());
        //parents.add(new GrandParent()); Not allowed

        ArrayList<Parent> p=new ArrayList<>();
        ArrayList<Child> c=new ArrayList<>();
        ArrayList<GrandParent> gp=new ArrayList<>();

        testExtendP(p);
        //testExtendP(c); Not allowed only super type allowed no child
        testExtendP(gp);

        testExtends(c);
        testExtends(p);
        //testExtends(gp); Not allowed because GrantParent does not extends Parent
    }

    /**
     * This Method allowed get operations for Parent 
     * No add 
     * 
     */
    public void testExtends(ArrayList<? extends Parent> list) throws Exception {
    /*  list.add(new Child());
        list.add(new Parent());
        list.add(new GrandParent());
        // can not add
        */
        Child c=(Child) list.get(0);
        Parent parent=list.get(0);
        GrandParent gp=list.get(0);
        /**
         * Unsafe collection way
         */
        ArrayList list2=new ArrayList();
        list.addAll(list2);
    }

    /**
     * This Method allowed add operations for Parent and it's sub types and on get it gives Object type 
     * 
     */
    public void testExtendP(ArrayList<? super Parent> list) throws Exception {
        list.add(new Child());
        list.add(new Parent());
        //list.add(new GrandParent());
        /*can not add because GrandParent can not be converted to Parent type
         * 
         * The method add(capture#3-of ? super Parent) in the type ArrayList<capture#3-of ? super Parent> is not applicable for the arguments (GrandParent)
         * 
         */

        Child c=(Child) list.get(0);
        Parent parent=(Parent) list.get(0);
        GrandParent gp=(GrandParent) list.get(0);
        Object obj=list.get(0);

        /**
         * Unsafe collection way
         */
        ArrayList list2=new ArrayList();
        list.addAll(list2);
    }

    /**
     * This Method allowed all operations for Parent and it's sub types 
     * 
     */
    public void testDirect(ArrayList<Parent> list) throws Exception {
        list.add(new Child());
        list.add(new Parent());
        //list.add(new GrandParent()); 
        /*
         * Can not add GrandParent (Normal generic rule)
         */
    }

}
class GrandParent{

}

class Parent extends GrandParent{

}
class Child extends Parent{

}
Ankit Katiyar
  • 2,631
  • 2
  • 20
  • 30