Today I come across a Function that makes me really wondering. So lets assume this simple structure for clearification.
public class Animal{
public String getName(){ return null; }
}
public class Dog extends Animal{
@Override
public String getName(){
//I'm aware that not any Dog's name is 'Pluto', but its just a Sample ;)
return "Pluto"
}
}
public class Cat extends Animal{
protected final String mName;
public Cat(String name){
mName = name;
}
@Override
public String getName(){
//cats have different names, because the internet loves cats
return mName;
}
public void miao(){
//just a dummy
}
}
Now it is absolut valid to assign a Dog to an Animal Pointer, but invalid to Assign an Animal to a Dog Pointer like this:
Animal animal = new Dog(); //valid, any Dog is at least an Animal
Dog dog = new Animal(); // invalid, of course not any Animal is a Dog!
Lets Assume an AnimalCage class, where the “Magic” happens:
public class AnimalCage{
private ArrayList<Animal> mCage = new ArrayList<Animal>();
public addAnimal(Animal animal){
mCage.add(animal);
}
// HERE is where the "Magic" happens:
public <A extends Animal> A getAnimalByName(String name){
//try catch block not mandatory
try{
for (Animal a: mCage){
if (name.equals(a.getName()) return (A)a;
}
} catch(ClassCastException cce){}
return null;
}
}
With the use of the AnimalCage it is possible to do this:
//all valid
AnimalCage cage = new AnimalCage();
Dog dog = new Dog();
Cat cat = new Cat("Mauzi");
Cat cat2 = new Cat("Garfield");
cage.add(dog);
cage.add(cat);
cage.add(cat2);
// and later get it back
//will return dog
Dog pluto = cage.getAnimalByName("Pluto");
//will find nothing and return null
Dog snoopy = cage.getAnimalByName("Snoopy);
//will raise ClassCastException and return null
snoopy = cage.getAnimalByName("Mauzi");
//will return Mauzi
Animal mauzi = cage.getAnimalByName("Mauzi");
so I can Do anything of this WITHOUT casting explicit. This leads me to the assumption, that Erasures aren’t erased at Runtime, although I know better. Before I thought I have to give at least an Indicator on what to Cast like this Function:
public <A extends Animal> A getAnimalByName(String name, Class<A> animalClass){
try{
for (Animal a: mCage){
if (name.equals(a.getName()) return (A)a; }
} catch(ClassCastException cce){}
return null;
}
//use
Dog dog = cage.getAnimalByName("Pluto", Dog.class);
I’m really wondering on how Java lets me assign Animals on Cats/Dogs, and on what specialization of Animal it has to Cast
I don’t quite understand your question, but perhaps I can clarify some points:
Singnature such as
<A extends Animal> A getAnimalByName(String name)involves a technique called type inference – i.e. actual type ofAin particular invocation ofgetAnimalByName()is inferred from the left side of assignment.Note that it’s a purely compile-time feature – code such as
turns into the following code when compiled (due to type erasure):
As you can see, your code breaks type safety guarantees – it happens when you do a cast in
return (A) a, and compiler emits a warning about it. It’s a basic guarentee of generics – if your code compiles without warnings, it doesn’t break type safety.