2
ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());

Why wouldnt this compile?

Ofek Ron
  • 8,354
  • 13
  • 55
  • 103

3 Answers3

5

Relevant part from the Java tutorial (http://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html):

A list defined by List can be informally thought of as read-only, but that is not a strict guarantee. Suppose you have the following two classes:

class NaturalNumber {

    private int i;

    public NaturalNumber(int i) { this.i = i; }
    // ...
}

class EvenNumber extends NaturalNumber {

    public EvenNumber(int i) { super(i); }
    // ...
}

Consider the following code:

List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35));  // compile-time error

Because List<EvenNumber> is a subtype of List<? extends NaturalNumber>, you can assign le to ln. But you cannot use ln to add a natural number to a list of even numbers.

The following operations on the list are possible:

  • You can add null.
  • You can invoke clear.
  • You can get the iterator and invoke remove.
  • You can capture the wildcard and write elements that you've read from the list.

You can see that the list defined by List is not read-only in the strictest sense of the word, but you might think of it that way because you cannot store a new element or change an existing element in the list.

Another relevant explanation can be found here (his link also explains the issue of wildcards - http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html):

...

It isn't safe to add arbitrary objects to it however:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

On the other hand, given a List, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.

shlomi33
  • 1,458
  • 8
  • 9
2

Since we don't know what the element type of array stands for, we cannot add objects to it.

Instead, use a temporary list:

ArrayList<A> tempArray = new ArrayList<A>();
tempArray.add(new A())
tempArray.add(new A())

ArrayList<? extends A> array = tempArray;

An example of how to use it

//Vehicle

public abstract class Vehicle {

}

//Car

public class Car extends Vehicle {

}

//HandlerVehicle

public class HandlerVehicle {

    private List<? extends Vehicle> _vehicles;

    public void addVehicles(List<? extends Vehicle> vehicles) {
        _vehicles = vehicles;
        //perform operations with Vehicle objects
    }

}

//HandlerCar

public class HandlerCar {
    private HandlerVehicle _handlerVehicle;
    private List<Car> _cars;

    public HandlerCar() {
        _cars = getCars();
        _handlerVehicle = new HandlerVehicle();
        _handlerVehicle.addVehicles(_cars);
    }

    private List<Car> getCars() {
        return new ArrayList<Car>();
    }
}
Víctor Albertos
  • 8,093
  • 5
  • 43
  • 71
  • 1
    What is the point of the last two lines of your code? You create an `ArrayList` and assign it to the reference `genericArray`. Then proceed to throw away the new list by assigning `genericArray` to refer to the same list as `array`. – Code-Apprentice Sep 07 '14 at 08:18
  • This will work and is no longer incorrect so I have removed my downvote. However, you can now use `tempArray` directly, so `array` is not really needed. Of course, maybe I'm missing a reason to do this. – Code-Apprentice Sep 07 '14 at 08:34
0

You cannot add an A to a List<? extends A>. In order to fix your problem you should simply declare your list as

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

Note that it is preferable to use an interface for the declaration:

List<A> array = new ArrayList<A>();

This allows you to easily change the concrete type at a later time because you only need to make a single change.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • There is nothing wrong with the declaration, the `array.add(new A());` is the problem since an `A` object cannot be added to a `List extends A>` – Henry Sep 07 '14 at 08:20
  • @Henry Hmm...I see. My solution still stands even though my explanation for the cause of the error is not valid. – Code-Apprentice Sep 07 '14 at 08:28
  • Assume `B` extends `A` and you have `List extends A> l = new ArrayList()`. It is clear that `l.add(new A())` would violate the integrity of the list. – Henry Sep 07 '14 at 08:28