Reputation: 1278
This is a group of classes that I made for context to the question
Aquarium class:
package test;
import java.util.ArrayList;
public class Aquarium {
// the aquarium is composed of fish tanks
ArrayList<Object> aquarium;
public Aquarium(){
aquarium = new ArrayList<Object>();
}
public <T> void addFishToTank(int index, T fish){
FishTank<T> tank = (FishTank<T>) aquarium.get(index);
tank.addFish(fish);
}
public ArrayList<Object> getAquarium(){
return aquarium;
}
public String toString(){
String holder = "";
int i = 0;
for (Object tank : aquarium){
holder += "Tank " + i + ":\n";
holder += tank.toString();
i ++;
}
return holder;
}
public static void main(String[] args){
Aquarium seaWorld = new Aquarium();
seaWorld.getAquarium().add(new FishTank<Salmon>());
seaWorld.addFishToTank(0, new Salmon());
seaWorld.addFishToTank(0, new Grouper());
System.out.println(seaWorld.toString());
}
}
FishTank class:
package test;
import java.util.ArrayList;
public class FishTank<T>{
// inside each fish tank there is a group of fish
ArrayList<T> fishGroup = new ArrayList<T>();
public void addFish(T element){
fishGroup.add(element);
}
public String toString(){
String holder = "";
for (T fish: fishGroup){
holder += fish.toString() + "\n";
}
return holder;
}
}
Salmon class:
package test;
public class Salmon {
public String toString(){
return "This is a salmon";
}
}
Grouper class:
package test;
public class Grouper {
public String toString(){
return "This is a grouper";
}
}
All the code compiles fine and the output of Aquarium ends up being
Tank 0:
This is a salmon
This is a grouper
How can this be possible? When I added the tank to the aquarium
seaWorld.getAquarium().add(new FishTank<Salmon>());
it was specified that it was a FishTank that can only hold Salmon objects. First, I added a Salmon object
seaWorld.addFishToTank(0, new Salmon());
and that would be fine since the fish tank at index 0 of the aquarium is a fish tank that holds Salmon objects. But when I add a Grouper object to the Salmon tank at index 0
seaWorld.addFishToTank(0, new Grouper());
it still adds the Grouper to the tank. Shouldn't an exception be thrown during run time? Then, how can I enforce so that only Salmon objects be added to the Salmon tank?
Upvotes: 4
Views: 291
Reputation: 5239
Generics are only for compile-time type safety. If you want an aquarium that has individually-typed tanks whose underlying fish types are not known at compile time, you are out of luck in Java. And to make matters worse, because the generic types are all erased during compilation, what you're left with is Object
everywhere.
The reason there is no class cast exception is because at runtime it is just a FishTank
(the raw type) at every array index, into which you can freely mix fish (or land animals, or psychological constructs).
One approach to solving this type of problem is to make your FishTank aware of its membership at runtime by including a Class< T >
field. The tank can then impose run-time checks during addFish()
, although the strictness of said checks is limited (for example, if your fish is itself a generic container, you'll still be able to mix containers in the same tank; getClass() returns an "erased" class object).
Upvotes: 4
Reputation: 33033
How can this be possible?
Because generic type parameters are erased at runtime, and are not checked on adding elements to a collection. They are only checked upon performing a cast. (But not a cast to T
, because T
is erased, so becomes just Object
, so such a cast would check nothing.)
Upvotes: 2