please see the following code:
import java.util.ArrayList;
public class Animal{...}
public class Dog{...}
public class TestAnimal{
public static void killAll(ArrayList <T extends Animal> animals){
System.out.println("animals are dead");
}
public static void main(String[] args){
ArrayList<Animal> simonAnimal = new ArrayList<>();
ArrayList<Dog> simonDog = new ArrayList<>();
killAll(simonAnimal);
killAll(simonDog);
}
}
the line that causes the problem is:
public static void killAll(ArrayList <T extends Animal> animals){
so what I want to do is to be able to use killAll() method on the any ArrayList that contains objects that are the sub class of Animal, in this case - the Dog class. I don't know what's wrong with my code. please help!
the error message is:
Incorrect number of arguments for type ArrayList; it cannot be parameterized with arguments <T, Animal>
I just replaced
<T extends Animal>
as
<? extends Animal>
it works, but can someone tell me why doesn't work?
You're trying to declare a type variable T and its bounds, and you're trying to use it all at once, which you can't do in Java generics. I don't get that error message; I get "unexpected bound".
First, declare the type variable and its bounds, in angle brackets before the return type, and refer to it as the type argument to the method parameter. This will remove the compiler error.
public static <T extends Animal> void killAll(ArrayList<T> animals) { ... }
But we can do better. First, program to the interface and use List<T>. Also, if you aren't using the specific type of T in the actual body, then you can use an upper-bounded wildcard instead of an explicit type variable.
public static void killAll(List<? extends Animal> animals) { ... }
The correct syntax for declaring a type variable on a method looks like this:
public static <T extends Animal> void killAll(ArrayList<T> animals){
System.out.println("animals are dead");
}
I know it might seem unintuitive, but even though Dog is a subtype of Animal, ArrayList is not a subtype of ArrayList. As pointed out by others, you can use a wildcard type.
ArrayList<? extends Animal> denotes any generic ArrayList type whose type parameter is a subclass of Animal such as ArrayList.
public static void killAll(List <? extends Animal> animals){
It is also better to use a List instead of a concrete implementation like ArrayList
The correct way to make generic methods like your example is this:
public static <T extends Animal> void killAll(List<T extends Animal> animals){#code}
As other colleagues told you, it is better to receive a list than an arraylist.
Related
class Employee<T extends Number> { // valid
}
class Employee<? extends Number> { // invalid
}
private static void test(List<? super Number> list1) { // valid
}
private static <T>void test(List<T super Number> list1) { // invalid
}
what exactly is difference between ? and T and when to use what?
Why with class definition, ? doesn't work but it works with List and why T works with class definition but not with List?
Where to declare a generic
You can not use a generic type token T before introducing it.
In your method example you try to declare the T at the wrong spot, that is invalid syntax. You have to introduce it beforehand.
For the class example however, you have put it in the right spot.
Here is where you can introduce your generic type token on a class wide level:
public class Foo< HERE > { ... }
and thats how you do it for a method only:
public < HERE > void foo(...) { ... }
Bounded generics
In both cases you can bound your T, like T extends Number and then use it accordingly:
public class Foo<T extends Number> { ... }
// or
public <T extends Number> void foo(...) { ... }
After you have introduced your T, you will use it just like that. So List<T>, as an example.
public <T extends Number> void foo(List<T> list) { ... }
Note that T super Number is invalid on as it makes little sense and does not provide more information than just T or Number or simply Object, depending on what you are trying to achieve. You can read more about that at Java generic methods: super can't be used?
Wildcards
Wildcards are a different thing. They are not a generic type token that you have to introduce first, such as T. Instead, they clarify the type range you want to accept.
For example a method like
public static void foo(List<? super Dog> list) { ... }
can be called with a List<Dog>, a List<Animal> or even a List<Object>. We call such a list a consumer of Dogs. To be precise, these are all lists that would accept a dog, so list.add(new Dog()) will work.
On the other side, we have
public static void foo(List<? extends Dog> list) { ... }
which can be called with a List<Dog> or also a List<Chihuahua>. We call such a list a producer (or provider) of Dogs. To be precise, these are all lists that can provide dogs. So Dog dog = list.get(0) will work.
You can read more about the details of what wildcards are and how they work at What is PECS (Producer Extends Consumer Super)?
When to use which?
In general, you would use a generic type token T when you actually still need to maintain the type safety throughout your code. I.e. when you need to be able to give the type a name. Otherwise you use wildcards ?.
For example, suppose you want to create a method that takes in a list and an element to add to it:
public static <T> void addToList(List<T> list, T element) {
list.add(element);
}
You need to introduce the T to ensure that the type of the list matches the given element. Otherwise someone could do addToList(dogs, cat), which you do not want.
If you do not have the need to actually name the type, you can also just use a wildcard. For example a method that takes a list and prints all its contents:
public static void printAll(List<?> list) {
for (Object object : list) {
System.out.println(object);
}
}
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?
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 Cats.
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();
}
I have a class that can a list of take both a generic Number and a Double to perform a certain calculation.
This works fine:
public class Test<T extends Number> {
public void testGeneric(List<T> list){
doTest(list);
}
public void testExplicit(List<Double> list){
doTest(list);
}
public void doTest(List<? extends Number> testList){}
}
However, if the argument in question is a nested type, it doesn't compile anymore:
public class Test<T extends Number> {
public void testGeneric(List<List<T>> list){
doTest(list);
}
public void testExplicit(List<List<Double>> list){
doTest(list);
}
public void doTest(List<List<? extends Number>> testList){}
}
Note that the functionality of the list doesn't matter, the second List type can be anything generic, for instance a wrapper around the T type. I don't really see why there should be any difference. Is there a way around this?
Thanks!
As you apparently have realized, a List<Double> is not a subtype of List<Number>; you need the wildcards to make the inheritance "propagate" into the generic types: a List<Double> is a subtype of List<? extends Number>. However, the wildcards must go all the way from the outermost level: a List<X> is a subtype of List<List<? extends Number>> only if X is exactly a List<? extends Number>. If you want to accept other subtypes of List<? extends Number>, such as List<Double>, you need List<? extends List<? extends Number>>.
I am trying to override the method which return type as list in sub class like as below example, due to generics issue i can not able to do it in my sub class. I can not able to change my super class code, so how i can resolve the issue? any one can please guide me...many thanks in advance.
Super classes which can not be update:
public class Animal {
private String legs;
}
public class TestSuper {
public List<Animal> addAnimals() {
return animals;
}
}
Sub classes:
public class Dog extends Animal {
private String sound;
}
public class TestSub extends TestSuper {
public List<Dog> addAnimals() { --> I need to override this method but want return type as List of dogs
return null;
}
}
If the super classes really can't be updated, I'm afraid you simply can't.
If you had been able to update the super classes:
In TestSuper, you would have use public List<? extends Animal> addAnimals() instead of public List<Animal> addAnimals().
What other solution(s) do you have:
You could use such a utility method:
#SuppressWarnings("unchecked")
public static <T extends Animal> List<T> cast(List<Animal> animals, Class<T> subclass) {
List<T> out = new ArrayList<T>();
for (Animal animal : animals) {
if (!subclass.isAssignableFrom(animal.getClass())) {
// the "animal" entry isn't an instance of "subclass"
// manage this case however you want ;)
} else {
out.add((T) animal);
}
}
return out;
}
Then, to call:
List<Dog> dogs = TheClassNameYouChose.cast(animals, Dog.class);
If Dog is a subclass of Animal, you could possibly do
The following signature:
public List<? extends Animal> addAnimals()
Then do
public List<Dog> addAnimals()
in the sub class.
Or you could make the super class generic <T> and the implementing class <Dog>
You cant change generic type of List to subtype of that generic type. Problem is that reference to any instance can be subtype of that instance. So lets have a look at this example
SuperType sup = new SubType();
sup.getAnimals().add(new Cat());
//adding cat is possible because getAnimals() returns List<Animal>
So you would add Cat to list that should contain only Dogs, which is wrong.
Also in overridden method you cant change generic type of list to list with supertype of that generic type (like List<Dog> to List<Animal>). Why? Lets take a look at this example
SuperType sup = new SubType();
Dog d = sup.getAnimals().get(0);
In subtype we have list of Animals so it is possible that get(0) will return Cat.
If you can change code in your classes then try maybe this approach
class Super<T extends Animal> {
public List<T> addAnimals() {
return null;
}
}
class Sub extends Super<Dog> {
#Override
public List<Dog> addAnimals() {
return null;
}
}
You would need to make the TestSuper use generics to really make this work correctly.
TestSuper<T extends Animal> {
public List<T> addAnimals() {
}
}
TestSub extends TestSuper<Dog> {
}
This is not possible. The super class contract says that if you have:
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
The following code must be valid:
TestSuper test = new AnySubclassOfTestSuper(); // including TestSub!
test.addAnimals().add(new Cat());
Your override in the TestSub class would violate that, since you can't add a Cat to a List<Dog>. Since it'd be a violation of a fairly fundamental principle of object-oriented type systems, you're not going to find a perfect workaround for this restriction.
The suggestions to change the superclass to use List<? extends Animal> would work because you can't add anything to that list.
protected List<? extends ErrorTO> errors = new ArrayList<ErrorTO>();
public abstract Collection<? extends ErrorTO> getErrors();
public void registerError(ErrorTO e) {
errors.add(e);
}
there is a compiling error of line "errors.add(e)", it is expecting some type of "? extends ErrorTO" and does not like ErrorTO? why and ho to fix that? Thanks!
The compiler doesn't know that getErrors will return a Collection<ErrorTo>; it might instead return a Collection<SomeSubclassOfErrorTo>. If you then tried to add an ErrorTo that wasn't of this subclass to the collection, type safety would be violated. For this reason, the compiler will not let you do this.
List<? extends ErrorTO> is not a List that can contain anything that extends ErrorTO.
It is a List of an (unspecified) subclass of ErrorTO.
if MyErrorTO extends ErrorTO, then I can create a List<MyErrorTO> and assign it to errors.
Your code will then try to add an ErrorTO to my List, which is illegal - my List can only contain a specific subclass of ErrorTO
You can't add anything to a List<? extends ErrorTO>. Make it a List<ErrorTO>.
List<? extends ErrorTO> means: a list of some unknow class, which is ErrorTO or extends ErrorTO. So obviously, if you could add something to it, its type-safety would be broken.
Maybe this will help a little. Lets say we have classes
class Fruit {}
class Pear extends Fruit{ void methodA(){} }
class Apple extends Fruit{ void methodB(){} }
and we want to create method like this
public static void method(List<? extends Fruit> list, Fruit f) {
list.add(f); //<-- why is this forbidden?
}
As list argument this method can accept lists like
ArrayList<Fruit>
ArrayList<Apple>
ArrayList<Pear>
If the argument would be new ArrayList<Pear>(); then compiler shouldn't allow to add elements like Fruit or Apple.
But compiler can't know what kind of list he will be dealing with. Because of that lack of knowledge it is impossible to add any elements via List<? extends Fruit> list reference.