membersound
membersound

Reputation: 86935

How to add items to a generic list of the same supertype?

I'd like to have a method in one of my services that should return a generic list. Then I want to add items to that list from another service.

class Fruit;
class Apple extends Fruit;

class FruitService() {
        private ArrayList<? extends Fruit> getList() {
            return new ArrayList<Apple>();
        }
}

class SomeService() {
        init() {
            fruitService.getList().add(new Apple());
        }
}

This gives the following error:

The method add(capture#3-of ? extends Fruit) in the type ArrayList<capture#3-of ? extends Fruit> is not applicable for the arguments (Apple)

Why? How could I add an Apple to that generic list?

My goal is to have that getList() method not to return a specific implementation.

Upvotes: 1

Views: 229

Answers (3)

zapl
zapl

Reputation: 63955

You can't.

ArrayList<? extends Fruit> 

could be in fact an

ArrayList<Apple> 

in which you can't insert a Banana although Banana extends Fruit. And you can't insert a Fruit because it has to be at least something that extends Apple. And since Java can't see the required type anymore but has to guarantee that it would work it won't even allow you to insert an Apple although that would be allowed for the actual list.

-> You can't insert anything into List<? extends Whatever> besides null because you don't know the exact type. ? extends Whatever results in a read-only list of Whatever.

If you want to return a regular & useful List don't return one with wildcard types.

Instead you could use generics in FruitService e.g.

class FruitService<T extends Fruit> {
    private ArrayList<T> getList() {
        return new ArrayList<T>();
    }

    public void useList(T fruit) {
        getList().add(fruit);
    }
}

class User {
    void foo() {
        FruitService<Apple> appleService = new FruitService<Apple>();
        appleService.useList(new Apple());

        FruitService<Banana> bananaService = new FruitService<Banana>();
        bananaService.useList(new Banana());
    }
}

Upvotes: 4

Bhesh Gurung
Bhesh Gurung

Reputation: 51030

You could make the method generic -

public <T extends Fruit> List<T> getList() {
    //....

The type variable T can capture the actual type argument, so the compiler can assume type-safety.

Also, you can do return new ArrayList<T>(); instead of return new ArrayList<Apple>();.

Upvotes: 2

nicholas.hauschild
nicholas.hauschild

Reputation: 42834

Can you have your getList() method return a List (or ArrayList) of Fruit instead? That way, you can insert any subtype of Fruit into the List.

private List<Fruit> getList() {
    return new ArrayList<Fruit>();
}

Upvotes: 2

Related Questions