With multiple level inheritance and polymorphism it can become a bit challenging to see how a method accepting and returning a generic would behave if within the method some transformation takes place and either a sub or super type of that generic is returned. Let’s take an example
Interface:
public interface ClassInterface {
String sayHello();
}
Super-class:
public class ClassSuper implements ClassInterface {
public String whoAmI = null;
public ClassSuper() {
whoAmI = "Super";
}
@Override
public String sayHello() {
return whoAmI + "says Hello!";
}
}
Sub-class:
public class ClassSub1 extends ClassSuper {
public ClassSub1() {
whoAmI = "Sub1";
}
}
Handler-class:
public class ClassHandler {
public T listenToOthers(T cls, String rType) {
ClassInterface cls1 = ((ClassInterface) cls);
cls1.sayHello();
// test by returning different class
if ("Sub".equals(rType)) {
return (T) new ClassSub1();
} else if ("Super".equals(rType)) {
return (T) new ClassSuper();
} else {
return cls;
}
}
public static void main(String[] args) {
try {
System.out.println("passed-in: Sub1, requested: Same");
System.out.print("got: " + new ClassHandler().listenToOthers(new ClassSub1(), "Same").whoAmI);
System.out.println("\n");
System.out.println("passed-in: Super, requested: Sub");
System.out.print("got: " + new ClassHandler().listenToOthers(new ClassSuper(), "Sub").whoAmI);
System.out.println("\n");
System.out.println("passed-in: Sub1, requested: Super");
System.out.print("got: " + new ClassHandler().listenToOthers(new ClassSub1(), "Super").whoAmI);
System.out.println("\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Result:
passed-in: Sub1, requested: Same got: Sub1 passed-in: Super, requested: Sub got: Sub1 passed-in: Sub1, requested: Super java.lang.ClassCastException: org.j2eebuilder.generics.ClassSuper cannot be cast to org.j2eebuilder.generics.ClassSub1 at org.j2eebuilder.generics.ClassHandler.main(ClassHandler.java:29)
Explanation:
So what happened here?
1. We passed-in T=Sub1 and told handler to return the same (=Sub1), so T supplied = T returned. No issues!
2. We passed-in T=Super and told handler to return Sub1, so T supplied = super of T returned. Thus casting (T=Super) Sub1 (in the method listenToOthers) worked!
3. We passed-in T=Sub1 and told handler to return T=Super, so T supplied = sub (not super) of T returned. Thus casting (T=Sub1) Super (in the method listenToOthers) failed!
As you can see one might incorrectly think that I am passing in a sub-class and so getting back a super-class should be fine – though as explained it is not!
Hope it helps understand how generics work in complex inheritance and polymorphism scenarios!