I am trying to create a generic method for sub classes lists which add a new element no matter which of the sub class I choose.
for that matter I made an example that will be easy to understand.
There is A zoo container of giraffes and zebras lists. zebra and giraffe are both Animals. I needed to create a'mate' method that will be able to get List of homo gene type meaning list of giraffe or zebra(but not both), and the mate method will add another animal of the type to the existing list (without copying) if there are more than 2 animals in the list.
so to solve this I thought to used reflection since I can not initiate a generic type in java(it is not allowed in java- new T() is not allowed).
so I created an instance of the first element, and tried to add it to the list with animals.add(newAnimal);.. however the compiler complains about it with the following error:
Error: java: no suitable method found for add(Animal)
method java.util.Collection.add(capture#1 of ? extends Animal) is not applicable
(argument mismatch; Animal cannot be converted to capture#1 of ? extends Animal)
method java.util.List.add(capture#1 of ? extends Animal) is not applicable
(argument mismatch; Animal cannot be converted to capture#1 of ? extends Animal)
I solve it by getting the add method at run-time using again, reflection, and it is working(the line in comment), however I would like to know why the compiler does not allow me to use add for animal, because I could not find the answer myself.
The code:
class Animal implements Comparable<Animal>{
int numLegs;
#Override
public int compareTo(Animal o) {
return this.numLegs-o.numLegs;
}
}
class Giraffe extends Animal{
int neckLength;
}
class Zebra extends Animal{
int numOfStripes;
}
class Zoo{
List<Giraffe> giraffes=new ArrayList<Giraffe>();
List<Zebra> zebras=new ArrayList<Zebra>();
public void printMostAnimals(){
for(Animal a: getMostAnimals()){
System.out.println(a);
}
}
private List<? extends Animal> getMostAnimals() {
if(giraffes.size()>zebras.size())
return giraffes;
return zebras;
}
public void mate(List<? extends Animal>animals) throws IllegalAccessException, InstantiationException {
if(animals.size()>2){
Class<?> firstAnimalInstanceClass=animals.get(0).getClass();
Animal newAnimal=(Animal)firstAnimalInstanceClass.newInstance();
animals.add(newAnimal);
// animals.getClass().getDeclaredMethod("add",Object.class).invoke(animals,newAnimal);
System.out.println("new mate was added");
return;
}
System.out.println("no mate been added");
}
}
class App{
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Zoo z=new Zoo();
z.zebras.add(new Zebra());
z.zebras.add(new Zebra());
z.zebras.add(new Zebra());
z.mate(z.zebras);
System.out.println("new zebra been added?"+(z.zebras.size()>3));
}
}
Thanks,
Because somebody might write:
List<Giraffe> giraffes = new ArrayList<>();
List<? extends Animal> animals = giraffes;
animals.add(new Zebra());
for (Giraffe g : giraffes) { // but there is a Zebra in there!
g.eatLeavesFrom(tallTree); // Zebras can't do that!
}
To have type safety when mating, you could do:
class Animal<M extends Animal<M>> {
M mate;
}
class Giraffe extends Animal<Giraffe> {}
class Zebra extends Animal<Zebra> {}
which allows you to write:
<A extends Animal<A>> void matingSeason(List<A> animals) {
A x = null;
for (A a : animals) {
if (x != null) {
a.mate = x;
x.mate = a;
x = null;
} else {
x = a;
}
}
}
you cannot add anything other than null to List<? extends Animal>.
Ex:
void foo(List<? extends Animal> animals ){
animals.add(new Animal());// compiler error
}
let's assume for a minute it doesn't raise a compiler error and i call foo(new ArrayList<Dog>()), it will add Animal to a List of dogs. clearly that shouldn't be allowed and thus the above code won't compile.
if this didn't raise an error the whole point of generic compile time safety is gone.
You are not allowed to add an Animal "aa" to a List<Giraffe> because "aa" could be a Zebra.
When you declare a List<? extends Animal>, you tell the compiler that it is a a List<Giraffe>, a List<Zebra> or a List<Animal>. Since it could be a List<Giraffe>, we fall in the case above, which means that you cannot add an Animal.
If you remove the generic, it should work, because you tell the compiler that there could be anything in the list:
((List)animals).add(newAnimal);
However you loose all type safety.
Instead why not do it as follows:
class AnimalList<AnimalType extends Animal> extends List<AnimalType> {
void mate() {
Class<AnimalType> firstAnimalInstanceClass = animals.get(0).getClass();
AnimalType newAnimal = (AnimalType)firstAnimalInstanceClass.newInstance();
add(newAnimal);
}
}
In your Zoo, you would declare:
AnimalList<Zebra>
AnimalList<Giraffe>
and then call mate without arguments:
AnimalList<? extends Animal> list;
list.mate();
Related
I have a tricky problem with Java generics, or maybe I can't see the wood for the trees.
I have three classes, A, B, and C that look as follows.
abstract class A<T extends A<T>> {
abstract T sefl();
};
abstract class B<T extends B<T>> extends A<T> {
};
class C extends B<C> {
#Override
C sefl() {
return this;
}
}
Later I have different versions of B's as well as different versions of C's. Furthermore, I have a function test that should accept a list of B's (or of one of its cousins). Or in general, it should accept any list of elements that inherit from A. Unfortunately, I need to know the type of the list's elements in the function's body, i.e. the uppermost type a.self can return (or the type T). Function test looks as follows:
static <T extends A<T>> void test(List<T> list) {
for (A<T> a : list) {
#SuppressWarnings("unused")
T t = a.sefl();
}
}
Now, calling test with a list of C's works.
List<C> cs = new LinkedList<C>();
test(cs);
But
List<B> bs = new LinkedList<B>();
test(bs);
results in a warning requiring a type parameter and
List<B<?>> bs = new LinkedList<B<?>>();
test(bs);
is not valid. Where is my mistake, or how can I create a list of B's that is accepted by function test?
Some words to the motivation behind this problem. The classes A, B, and C (or Animal, Mammal, and Cat) implement a tree-like data structure, where each class extends the structure with some properties. Typically, all super-classes are abstract and you can only create instances from leaf-classes, e.g. cat. Now, the difficulty is that the classes implement a copy-on-write policy (COW), i.e. modifying an object creates and returns a new instance of itself with the modified property.
For example, let’s say all animals have an age property. You can easily define this property in Animal, and you can provide a getter method to return the age.
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
};
However, how do you define the setter method? You can write it as follows:
public abstract Animal setAge();
This requires that (at least) each non-abstract element must implement the setter function. For example:
class Cat extends Mammal<C> {
#Override
public Animal setAge(int age) {
return new Cat(/* .. */);
}
}
Remember, as we implement a COW policy, we must create a new instance. So, in the setter function (e.g. implement in Cat) we return a new cat with the new age. Calling cat.setAge(4) on a Cat element returns a new Cat. Unfortunately, because of the type signature, we only now that we got an Animal returned from setAge, even if we call it on a Cat directly. The twist with the generics helps to reveal the concrete type when calling setAge. So, we can construct Animal like this:
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
public abstract T setAge();
};
And in Cat we can say:
class Cat extends Mammal<C> {
#Override
public Cat setAge(int age) {
return new Cat(/* .. */);
}
}
So, back to the problem. Your right, using List<? extends Animal<?>> as the type of the list works, but unfortunately, I need some way to know the type of the elements. Or more concrete: Function test must replace the old element with the new one. For example:
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
#SuppressWarnings("unused")
Animal<?> a = animal.setAge(4711);
list.add(a);
}
}
And unfortunately, the list extension list.add(a); ist the statement that doesn't work with this signature.
Well, they are two very different implementations:
class C ...
and
class B<T extends B<T>> ...
The class C doesn't declare any generic type.
Simple letters for class names are a bit confusing here, so let's do:
abstract class Animal<T extends Animal<T>> {
abstract T sefl();
};
abstract class Mammal<T extends Mammal<T>> extends Animal<T> {
};
class Cat extends Mammal<Cat> {
#Override
Cat sefl() {
return this;
}
}
So:
List<Cat> catList = new LinkedList<>();
works well, as there is no generic type involved. The compiler determines that
Cat extends Mammal<Cat> ( == Cat extends Animal<Cat> )
fits within the bounds <T extends Animal<T>>
On the other hand for
List<Mammal> mammalList = new LinkedList<>();
test(mammalList); // ok, but mammal list of what???
the compiler can't match the bounded types.
In fact, Mammal<T extends Mammal<T>> extends Animal<T> doesn't have anything to do with <T extends Animal<T>>.
Even by providing a wildcard, you'll never be able to pass a List<Mammal<?> to test. The method signature rejects it!
A possible solution:
A more generic test method
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
Animal a = animal.sefl();
}
}
can be used along with different List types:
List<? extends Mammal<?>> bs = new LinkedList<>();
test2(bs);
List<Cat> catList = new LinkedList<>();
test2(catList);
List<Animal<Cat>> animalList = new LinkedList<>();
test2(animalList);
Java version:
java 9.0.4
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
Please help me solve the following:
class Cat must extend Animal (keep inheritance);
Animal.getAll() must return all animals (cats, dogs, etc.) and Cat.getAll() must return only cats (don't alter the signature).
I've tried and failed with error The return type is incompatible with Animal.getAll() on line 8.
class Animal {
public static List<Animal> getAll() {
return new ArrayList<Animal>();
}
}
class Cat extends Animal {
public static List<Cat> getAll() {
return new ArrayList<Cat>();
}
}
Change line 2 to:
public static List<? extends Animal> getAll() {
it will work.
See Java Wildcards in Generics for more information
I will show you two style of declaration of generics. In part1, I'm using generic upper boundary declaration on List as follows:
List<? extends Animal> totList = new ArrayList<Animal>();
But this will throw error like below if you try to add a Animal object to the list:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method add(capture#1-of ? extends Animal) in the type List<capture#1-of ? extends Animal> is not applicable for the arguments (Animal)
at GenericsType.main(GenericsType.java:39)
But as in Part2, if I declare the list inside a generic class in the format below, no errors are thrown while adding (Animal objects) or (subclass of Animal objects) to the list.
class GenericAnimal<T extends Animal>
{
List<T> genList = new ArrayList<T>();
}
Why in part2, it didn't throw error and what is the difference between two style of declaration.
Example Code:
1.Animal.java
public class Animal {
private String name;
private int height;
public void animalJump()
{
if(height>100)
{
System.out.println(name+" with height-"+height+" can JUMP");
}
else
System.out.println(name+" with height-"+height+" cannot jump");
}
public void setName(String name) {
this.name = name;
}
public void setHeight(int height) {
this.height = height;
}
public Animal(String name, int height) {
setName(name);
setHeight(height);
}
}
2.GenericsType.java
import java.util.*;
import Animal;
import Animal.Cannine;
import Animal.Feline;
import Animal.Feline.Cat;
import Animal.Cannine.Dog;
public class GenericsType {
public static List<? extends Animal> totList = new ArrayList<Animal>();
public static void processAllfunc1()
{
for(Animal a : totList)
{
a.animalJump();
}
}
public static void main(String args[])
{
// Part 1
totList.add(new Animal("Animal1",21)); // Error: Unresolved compilation problem:
processAllfunc1();
// Part 2
GenericAnimal<Animal> genericanimal = new GenericAnimal<Animal>();
genericanimal.genList.add(new Animal("Animal2",22)); // No Error, why?
genericanimal.genList.add(new Cat("Cat4",204)); // No Error for Cat also, why?
genericanimal.processAllfunc2();
}
}
3.GenericAnimal.java
public class GenericAnimal<T extends Animal> {
public List<T> genList = new ArrayList<T>();
public void processAllfunc2() {
for (T a : genList) {
a.animalJump();
}
}
}
In part 2, the type of genericanimal.genList is List<T> = List<Animal>. In part 1, the type of the list is List<? extends Animal>.
The issue is that List<? extends Animal> means "a list of some specific subtype of Animal which is unknown." For example, you could write List<? extends Animal> list = new ArrayList<Cat>(). And you shouldn't be able to add any animal to a list of cats. By writing List<? extends Animal>, you're saying that you want to deliberately lose track of which type of animal is allowed into the list, though you know that whatever's in the list is some type of animal.
Why in part1, it threw error while trying to add an Animal object to the list?
List<? extends Animal> totList = new ArrayList<Animal>();
totList.add(new Animal("Animal1",21)); // Error: Unresolved compilation problem:
Because compiler restricts addition of objects on list declared using upper bounded wildcard( ? extends Animal). Compiler doesnt know for sure if the list is typed to the Animal, or Cat or Dog.
Simple, whenever a variable is declared using Upper bounded Wildcard, then inserting elements is not allowed to that variable.
Advantages of using Upper bounded Wildcard is that:
You can create a method that can just read all elements (but no
insertions) in the List.
You can reuse that same method for the list of Animal or list of Dog
or list of Cat.
Also you can safely apply all methods of Animal on elements.
Why in part2, it didn't throw error while adding an Animal object and also Cat object to the list?
public List<T> genList = new ArrayList<T>();
genericanimal.genList.add(new Animal("Animal2",22)); // No Error, why?
genericanimal.genList.add(new Cat("Cat4",204));
Because Here genList is actually of type Animal like in below format
List<Animal> genList = new ArrayList<Animal>();
Now just like normal list of specific type, it can add elements of that type.
So an Animal object can be added to List
Also Cat object is allowed to be added since it is also an Animal object only (Cat is just a subclass of Animal)
Advantages of using <T extends Animal> is that:
You can read all elements of the List.
You can insert elements to the List, but the element must be of type
T.
You can reuse the methods for Animal objects or Cat objects or Dog
objects.
You can safely apply all the Animal methods on elements.
I know that there are a lot of questions about this topic, but unfortunately they couldn't help me to eliminate my obscurities. First of all, look at the following example. I don't understand, why the following "add"-method someCage.add(rat1) doesn't work and aborts with the following exception:
Exception in thread "main" java.lang.Error: Unresolved compilation
problem: The method add(capture#2-of ? extends Animal) in the type
Cage is not applicable for the
arguments (Rat)
Is this the same reason why Cage<Rat> is not a Cage<Animal>? If yes, I don't understand it in this example, so I'm not sure what the compiler exactly does. Here is the code example:
package exe;
import cage.Cage;
import animals.Animal;
import animals.Ape;
import animals.Lion;
import animals.Rat;
public class Main {
public static void main(String[] args) {
Lion lion1 = new Lion(true, 4, "Lion King", 8);
Lion lion2 = new Lion(true, 4, "King of Animals", 9);
Ape ape1 = new Ape(true, 2, "Gimpanse", true);
Ape ape2 = new Ape(true, 2, "Orang Utan", true);
Rat rat1 = new Rat(true, 4, "RatBoy", true);
Rat rat2 = new Rat(true, 4, "RatGirl", true);
Rat rat3 = new Rat(true, 4, "RatChild", true);
Cage<Animal> animalCage = new Cage<Animal>();
animalCage.add(rat2);
animalCage.add(lion2);
Cage<Rat> ratCage = new Cage<Rat>();
ratCage.add(rat3);
ratCage.add(rat1);
ratCage.add(rat2);
// ratCage.add(lion1); //Not Possible. A Lion is no rat
Cage<Lion> lionCage = new Cage<Lion>();
lionCage.add(lion2);
lionCage.add(lion1);
Cage<? extends Animal> someCage = new Cage<Animal>(); //? = "unknown type that is a subtype of Animal, possibly Animal itself"
someCage = ratCage; //OK
// someCage = animalCage; //OK
someCage.add(rat1); //Not Possible, but why?
animalCage.showAnimals();
System.out.println("\nRatCage........");
ratCage.showAnimals();
System.out.println("\nLionCage........");
lionCage.showAnimals();
System.out.println("\nSomeCage........");
someCage.showAnimals();
}
}
This is the cage class:
package cage;
import java.util.HashSet;
import java.util.Set;
import animals.Animal;
public class Cage<T extends Animal> { //A cage for some types of animals
private Set<T> cage = new HashSet<T>();
public void add(T animal) {
cage.add(animal);
}
public void showAnimals() {
for (T animal : cage) {
System.out.println(animal.getName());
}
}
}
Moreover, I would be pleased if you could give me a meaningful "super" example with this animal-cage-code. Until now I haven't understood how to use it. There are a lot of theoretical examples and I read about the PECS concept but anyhow I wasn't able to employ it in a meaningful matter yet. What would it mean to have a "consumer" (with super) in this example?
Example of super bound
The introduced transferTo() method accepts Cage<? super T> - a Cage that holds a superclass of T. Because T is an instanceof its superclass, it's OK to put a T in a Cage<? super T>.
public static class Cage<T extends Animal> {
private Set<T> pen = new HashSet<T>();
public void add(T animal) {
pen.add(animal);
}
/* It's OK to put subclasses into a cage of super class */
public void transferTo(Cage<? super T> cage) {
cage.pen.addAll(this.pen);
}
public void showAnimals() {
System.out.println(pen);
}
}
Now let's see <? super T> in action:
public static class Animal {
public String toString() {
return getClass().getSimpleName();
}
}
public static class Rat extends Animal {}
public static class Lion extends Animal {}
public static class Cage<T extends Animal> { /* above */ }
public static void main(String[] args) {
Cage<Animal> animals = new Cage<Animal>();
Cage<Lion> lions = new Cage<Lion>();
animals.add(new Rat()); // OK to put a Rat into a Cage<Animal>
lions.add(new Lion());
lions.transferTo(animals); // invoke the super generic method
animals.showAnimals();
}
Output:
[Rat, Lion]
Another important concept is that while it is true that:
Lion instanceof Animal // true
it is not true that
Cage<Lion> instanceof Cage<Animal> // false
It this were not the case, this code would compile:
Cage<Animal> animals;
Cage<Lion> lions;
animals = lions; // This assignment is not allowed
animals.add(rat); // If this executed, we'd have a Rat in a Cage<Lion>
You can add a Rat to a Cage<Rat> (of course).
You can add a Rat to a Cage<Animal>, because a Rat "is" an Animal (extends Animal).
You cannot add a Rat to a Cage<? extends Animal>, because <? extends Animal> might be <Lion>, which a Rat is not.
In other words:
Cage<? extends Animal> cageA = new Cage<Lion>(); //perfectly correct, but:
cageA.add(new Rat()); // is not, the cage is not guaranteed to be an Animal or Rat cage.
// It might as well be a lion cage (as it is).
// This is the same example as in Kaj's answer, but the reason is not
// that a concrete Cage<Lion> is assigned. This is something, the
// compiler might not know at compile time. It is just that
// <? extends Animal> cannot guarantee that it is a Cage<Rat> and
// NOT a Cage<Lion>
//You cannot:
Cage<Animal> cageB = new Cage<Rat>(); //because a "rat cage" is not an "animal cage".
//This is where java generics depart from reality.
//But you can:
Cage<Animal> cageC = new Cage<Animal>();
cageC.add(new Rat()); // Because a Rat is an animal.
Imagine having your Cage<? extends Animal> created by an abstract factory method, which gets implemented by a subclass. In your abstract base class you cannot tell which type actually gets assigned, neither can the compiler, because maybe the concrete class gets only loaded at runtime.
That means, the compiler cannot rely on Cage<? extends Animal> to not be a Cage of some other concrete subtype, which would make the assignment of a different subtype an error.
Both answers so far have been great. I'd just like to add a tidbit to help your understanding of them.
To further Ron's answer, you may be thinking the following:
"why is it that that someCage.add(rat1) becomes a Cage<? extends Animal>.add(rat1)? Can't someCage point to any Cage of any type which extends Animal (and I've now set it to point to a cage of rats?)"
Totally legitimate question. Thing is, when you do the someCage = ratCage, an element-by-element copy is done from ratCage into someCage. So in fact, you have not simply set someCage to now point to a ratCage. In actuality, someCage is still a Cage<? extends Animal>. You can't do someCage.add(rat1) because you don't know the type of the Cage, only that it's type is bounded above by Animal.
P.S.: You can't add anything to someCage since its type is unknown
I think your question can be answered by the following snippet of code:
Cage<? extends Animal> cage = new Cage<Lion>();
cage.add(rat1);
You can clearly see that the code above shouldn't be valid, since you know that cage currently is a lion cage, and you shouldn't be allowed to add a rat to a lion cage.
The compiler does not what value you have assigned to the cage, so it can't allow cage.add(rat1) even if you assign a rat cage to the cage.
Here is my class Structure, and where I am getting error
class SuperClass{
//variables
}
class SubClass1 extends SuperClass{
//variables
}
class SubClass2 extends SuperClass{
//variables
}
class AClass{
List<SuperClass> list;
public AClass(boolean b){
if(b)
list = new ArrayList<SubClass1>();//getting error here
else
list = new ArrayList<SubClass2>();//and here
}
void addObjects(SuperClass obj){
list.add(obj);
}
}
How can I solve this? Should I change my design ? How?
ADDITION:
When I changed
`List<SuperClass> list;`
to
List<? extends SuperClass> list;
I am not getting the previous error but I am getting another error while adding objects,
The method add(capture#2-of ? extends SuperClass) in the type List<capture#2-of ? extends SuperClass> is not applicable for the arguments (SuperClass)
It seems you're trying to create a list that only contains objects from the particular subclass. In this case you just need the generics to play nice at compile time. (Generics are erased at runtime :) )
class AClass<T extends SuperClass> {
List<T> list;
public AClass(){
list = new ArrayList<T>();
}
void addObjects(T obj){
list.add(obj);
}
}
You should use a bounded wildcard in your ArrayList declaration:
class AClass{
List<? extends SuperClass> list;
public AClass(boolean b){
if(b)
list = new ArrayList<SubClass1>();
else
list = new ArrayList<SubClass2>();
}
}
}
The ? is a wildcard and defines an unknown type. But by using a bounded wildcard you can assure that it is an unknown subtype of SuperClass.
For further information about wildcards see here.
Concerning you're other problem:
The type of the parameter to list.add() is ? extends
SuperClass-- an unknown subtype of SuperClass.
Since we don't know what type it is,
we don't know if it is a supertype. it might or might not be
such a supertype, so it isn't safe to
pass a SubClass1 or SubClass2 there.