Reputation: 443
I'm looking for a type-safe, checked solution to add an element to a list whose generic requires both a class and an interface. The example illustrates what I'd like to do - add an object whose type Cat
extends Animal
and implements Quadruped
to a list List<T>
where T extends Animal & Quadruped
.
class Example {
public interface Quadruped { };
public static class Animal { };
public static class Cat extends Animal implements Quadruped { };
public <T extends Animal & Quadruped> List<T> getQuadrupedAnimals(){
List<T> result = new ArrayList<>();
Cat cat = new Cat();
result.add(cat); // compile error
return result;
}
}
Of course casting cat
to B
would resolve the compile error but that would be unchecked.
Is there any solution for this at all? If not, does anyone know the reason why the compiler does not allow this?
Upvotes: 1
Views: 116
Reputation: 140564
does anyone know the reason why the compiler does not allow this?
T
is a specific Animal and Quadraped, chosen at the call site of getQuadrupedAnimals()
.
Legal invocations of that method include:
List<Dog> listOfDogs = getQuadrupedAnimals();
List<Hamster> listOfHamsters = getQuadrupedAnimals();
so you can't put a Cat
into that list (nor a Dog
nor a Hamster
, for that matter), because whatever you put in there might not be castable to the type the caller wants. (You can put literal null
in the list, though).
The solution is to declare the return type as List<Cat>
(or List<? extends Cat>
, or List<? super Cat>
), if you want to return a list containing Cat
s.
An intersection type is only really useful if you use it in a parameter, e.g.
public <T extends Animal & Quadruped> List<T> getQuadrupedAnimals(T animal){
return List.of(animal);
}
or
public <T extends Animal & Quadruped> int countLegs(List<T> animals){
return 4 * animals.size();
}
Upvotes: 3