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!