Java Generics WildCard Question: List<? extends A>

12,379

Solution 1

Method arguments are contravariant in the subtype, and by the definition of the wildcard, for every type T that extends Vehicle Foo<T> is a subtype of Foo<* extends Vehicle>. The implication of this is that wildcards are great when you only care about the return type, but dont work in situations like this when you want to pass a value of the type to a method.

The problem is that a user might try to call

List<SpaceShip> l = ...
addCars(l);

if your code were to compile, l would then be a list of spaceships containing 3 cars. Clearly no good.

Solution 2

Here's a pointer to why you're getting a compile error. Specifically,

List is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.

There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method. For instance, this is not allowed:

public void addRectangle(List<? extends Shape> shapes) {
     shapes.add(0, new Rectangle()); // Compile-time error! 
} 

You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is ? extends Shape-- an unknown subtype of Shape. Since we don't know what type it is, we don't know if it is a supertype of Rectangle; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.

Solution 3

The supplied List is a list of some specific type of Vehicle (where, for the sake of argument we will refer to the type as T), but that specific type T is not known; it may be List<Vehicle>, List<Car>, etc. Therefore, since the specific generic type of the list is unknown, it is not permitted to invoke any method which requires the specific T as an argument. Only methods which don't involve T as an argument can be invoked.

The practical upshot of this, in the case of List, is that this prevents anything being added to the list - the list is not writable. On the other hand, the list can be read, but with the returned objects only known as Vehicle.

That is, the unknown type T cannot be supplied to the the List, but its known superclass of Vehicle can be returned by the list.

By way of example, given your method:

private static void addCars(List<? extends Vehicle> vcls) {

you could conceivably invoke:

List<Car> cars=new ArrayList<Car>();
addCars(cars);

which you intuit should be permitted. However, since addCars knows the list only as "some subtype of Vehicle", it can't be allowed to add objects to the list since the following invocation would then be just as valid:

List<Spaceship> ships=new ArrayList<Spaceship>();
addCars(ships);

whereby it becomes clear that it must be as mistake to try to add Car objects to a List under the guise of being a list of Vehicle objects.

Solution 4

private static void addCars(List<? extends Vehicle> vcls){

should be

private static void addCars(List<? super Vehicle> vcls){

This will fix the compile time error.

EDIT: Read here.

Solution 5

You can either use:

private static void addCars(List<? super Vehicle> vcls)

(which means the caller is to pass a list of objects that are Vehicle or a super type)

or

private static void addCars(List<Vehicle> vcls)
Share:
12,379

Related videos on Youtube

Saher Ahwal
Author by

Saher Ahwal

Software Engineer at Microsoft LinkedIn Profile MEng Thesis: Optimizations to a massively parallel database and support of a shared scan architecture

Updated on May 26, 2022

Comments

  • Saher Ahwal
    Saher Ahwal almost 2 years

    Let's say I have these classes : Vehicle, Car and Spaceship:

    class Vehicle{
    
        void rideVehicle(Vehicle v){
            System.out.println("I am riding a vehicle!");
        }
    
    }
    
    class Car extends Vehicle{
        void rideVehicle(Vehicle c){
            System.out.println("I am riding a car!");
        }
    }
    
    
    class SpaceShip extends Vehicle{
        void rideVehicle(Vehicle c){
            System.out.println("I am riding a spaceship!");
        }
    
    }
    

    and I write this method addCars:

    private static void addCars(List<? extends Vehicle> vcls){
            vcls.add(new Car());
            vcls.add(new Car());
            vcls.add(new Car());
    
        }
    

    Why do I get a compile time error?? I understand that List is a supertype of List for any X that extends Vehicle. right?

    Thanks

    Edit: the error I get (compile-time) : The method add(capture#2-of ? extends Vehicle) in the type List is not applicable for the arguments (Car).

  • Saher Ahwal
    Saher Ahwal about 13 years
    Thanks I know that, but I was confused of why the first doesn't work
  • Prince John Wesley
    Prince John Wesley about 13 years
    @Saher: ? extends means get only. only exception is that you can set null.