Reputation: 8395
I am trying to learn Subtyping in Java and I am not an better person in generics so I am getting this issue or doubt-
import java.util.ArrayList;
import java.util.Collection;
interface Animal<T>{}
class Lion implements Animal<Lion>{}
class Butterfly implements Animal<Butterfly>{}
class Cage<T>{
public <T> void addAnimal(T t){
}
}
interface CageAnimal<E> extends Collection<E>{}
public class SubType<T> {
public <T> SubType() {
Lion lion = new Lion();
Butterfly butterfly = new Butterfly();
/**
* **Here inside Lion cage, we added Butterfly : WRONG**
*/
Cage<Lion> cageLion = new Cage<Lion>();
cageLion.addAnimal(lion);
cageLion.addAnimal(butterfly);
CageAnimal<Lion> cageAnimalLion = (CageAnimal<Lion>) new ArrayList<Lion>();
cageAnimalLion.add(lion);
//cageAnimalLion.add(butterfly);//Butterfly is Not Supposed to add here as it is the cage of Lion
}
}
In the above example when I declare Cage , why I am able to add Butterfly and in the Same case when I made CageAnimal type, I am not able to add any Buttefly
Cage<Lion> cageLion = new Cage<Lion>();
cageLion.addAnimal(lion);
cageLion.addAnimal(butterfly);
and in case of Cage
Cage<Animal> cageAnimalLion = new Cage<Lion>();
cageAnimalLion.addAnimal(lion);
cageAnimalLion.addAnimal(butterfly); //Throwing Compile Error
Upvotes: 1
Views: 448
Reputation: 14257
Declare Cage
class like this:
class Cage<T extends Animal> {
public void addAnimal(T t) { ... }
}
If you declare the addAnimal
method in the following way...
public void <T> addAnimal(T t)
... you are "hiding" the T
type parameter with a different type parameter with the same name. It is the same as if you declared the method like this:
class Cage<T extends Animal> {
public void <X> addAnimal(X t) { ... }
}
...which is obviously not doing its job. On the other hand, in the first version I wrote, both the T
in declaration of the class and the method are the same.
Moreover declaring <T extends Animal>
bound ensures that the cage can only be of type that extends an Animal
, i.e. Cage<Lion>
, Cage<Butterfly>
, but Cage<String>
is illegal.
And of course, you cannot cast an ArrayList
to CageAnimal
, that will fail at runtime with a ClassCastException
, because ArrayList
in not a subtype of CageAnimal
.
Upvotes: 4
Reputation: 94499
class Cage<T>{
public <T> void addAnimal(T t){
}
}
The Cage class has a generic method addAnimal. The generic type associated with the method causes the generic type associated with the class to be ignored and the type of the parameter to be used as the generic type for the method.
Try executing the following example to see what is happening:
public class TestCage {
/**
* @param args
*/
public static void main(String[] args) {
Cage<String> cage1 = new Cage<String>();
cage1.addAnimal(new String("test1"));
cage1.addAnimal(new Integer(1));
cage1.addAnimal2(new String("test2"));
//cage1.addAnimal2(new Integer(1)); //Uncomment to throw error
}
}
class Cage<T>{
public <T> void addAnimal(T t){
System.out.println("T: " + t.getClass().getName());
}
public void addAnimal2(T t){
System.out.println("T: " + t.getClass().getName());
}
}
In summary, by adding a generic method to the class, the generic type parameter of the class is ignored and the type of the parameter passed into the method is used as the generic type parameter of the method.
Upvotes: 2
Reputation: 13114
By declaring public <T> void addAnimal(T t)
you're parameterising the method as well as the class Cage
. This T
has no relation to the T
in Cage<T>
.
You can either have:
class Cage<T extends Animal<T>> {
public void addAnimal(T animal) {
}
}
or, if you want the Animal
returned then have:
class Cage<T extends Animal<T>> {
public T addAnimal(T animal) {
}
}
Upvotes: 2
Reputation: 11996
Your problem is that fundamentally your Cage
will accept any T
, and therefore any Animal
. The various T
's don't all refer to the same value of T, they're variables local to the class or method.
What you could write is something like this:
public class Cage<T> {
public void addAnimal(Animal<T> caged) {
}
}
Now you will at least get compiler errors in the common case of:
Cage<Lion> c=new Cage<Lion>();
c.add(new Butterfly()); // should error AFAIK
However it will be reduced to a warning in case of:
Animal butterfly=new Butterfly();
Cage<Lion> c=new Cage<Lion>();
c.add(butterfly); // warning about raw types... IIRC
Because, fundamentally Cage
will still accept any Animal
.
EDIT: Note that the earlier mentioned answer of removing the <T>
local to the addAnimal
method will work better for this purpose.
Upvotes: 2
Reputation: 9920
Because CageAnimal
and Cage
are very different things. Looks how you've defined generic parameter for Cage
:
public <T> void addAnimal(T t){
}
This <T>
you put on the method, means that method has its own generic parameter, different from the one you've defined in class. If you remove it from method signature it will use generic parameter of the class.
E.g.
public void addAnimal(T t)
Upvotes: 3
Reputation: 198211
This line
public <T> void addAnimal(T t){
should probably be
public void addAnimal(T t){
Upvotes: 4