Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding objects to the "? extends" type of Generic Collections

public void addAllAnimals(ArrayList<? extends Animal> animalLikeList){

 // I need to add animal objects (eg Dog, Cat that extends Animal) to animalLikeList.

}

I know that it doesn't allow to add directly since ? extends Animal stands for unknown sub type of the Animal.

My question is : Isn't there any way (indirect way) to add or addAll an Animal or a SubType of Animal object to the animalLikeList ?

like image 989
namalfernandolk Avatar asked Jun 20 '12 12:06

namalfernandolk


People also ask

Can a generic class be extended?

We can add generic type parameters to class methods, static methods, and interfaces. Generic classes can be extended to create subclasses of them, which are also generic.

How do you add two generic values in Java?

You have to add the numbers as the same type, so you could do x. intValue() + y. intValue(); .

Is ArrayList a generic collection?

(ArrayList API documentation) ArrayList is a generic container that gives us array-like convenience for accessing elements (the method . get(i) gives us access by index) with linked-list like convenience for adding new elements (the method .


1 Answers

No, there is no direct legal way (apart from unsafe type casts). You can only add elements to a generic collection declared with super, not extends. This may be easier to remember with the acronym PECS (Producer - Extends, Consumer - Super), popularized by Josh Bloch in Effective Java, 2nd Edition, Item 28.

In fact what you seem to be after doesn't seem to make direct sense. A List<? extends Animal> can be e.g. a List<Dog> or a List<Cat>. The former can only accept Dog elements, the latter only Cats. And since you can't instantiate generic (nonreifiable) types in Java, you must know at runtime that you are dealing with a list of dogs, so you only attempt to add dogs to it (and vice versa for cats). That means you can't locally and statically create the objects to be added - you must get them in a generic way too, and moreover in a way which allows the compiler to ensure that the concrete types match each time. The simplest way for this is to pass the element(s) as a generic parameter. So it is possible and safe to do this:

public <E extends Animal> void addAllAnimals(List<E> animalLikeList, E animal) {
    animalLikeList.add(animal);
}

List<Dog> dogs = new ArrayList<Dog>();
List<Cat> cats = new ArrayList<Cat>();

addAllAnimals(dogs, new Dog());
addAllAnimals(cats, new Cat());

Note that this method is generic, with a named type parameter, which ensures that the actual generic type of the list is the same as the element type to be added to it.

You can trivially replace the E parameter with a Collection<E> to add several objects at once.

like image 163
Péter Török Avatar answered Nov 15 '22 20:11

Péter Török