Java generics: return bounded generic types
I follow this Code:
public <T extends ParentException> T managedException(Exception cause) { if(ExceptionA.class.isinstance(cause)) { return ExceptionA.class.cast(cause); } else if(ExceptionB.class.isinstance(cause)) { return ExceptionB.class.cast(cause); } else if(ExceptionC.class.isinstance(cause)){ return ExceptionC.class.cast(cause); } else { return new ExceptionD(cause.getMessage(),cause); } }
Here, exceptiona, exceptionb, exceptionc and exceptiond are the children of parentexception
When compiling, I got an error:
incompatible types: ExceptionA cannot be converted to T incompatible types: ExceptionB cannot be converted to T incompatible types: ExceptionC cannot be converted to T incompatible types: ExceptionD cannot be converted to T
However, if I change the code to:
@SuppressWarnings("unchecked") public <T extends ParentException> T managedException(Exception cause) { if(ExceptionA.class.isinstance(cause)) { return (T) ExceptionA.class.cast(cause); } else if(ExceptionB.class.isinstance(cause)) { return (T) ExceptionB.class.cast(cause); } else if(ExceptionC.class.isinstance(cause)){ return (T) ExceptionC.class.cast(cause); } else { return (T) new ExceptionD(cause.getMessage(),cause); } }
It has no compilation errors
As mentioned in the answer of so thread: @ L_ 301_ 0 @, it is allowed to use t for conversion, and give another pointer in this thread: Java genetics: generic type defined as return type only But my question is: when t is bounded and all returned objects fall, why do I need to use type conversion to enter the specified boundary?
Solution
What you are doing is wrong That's why you got wrong You can use exceptionc exceptionc = managedexception (exceptiond d) to call your method, and you will eventually get a cast (exceptionc) exception D; And it masks errors, but you get it at run time
Change your method to:
public ParentException managedException(Exception cause) { if(ExceptionA.class.isinstance(cause)) { return ExceptionA.class.cast(cause); } else if(ExceptionB.class.isinstance(cause)) { return ExceptionB.class.cast(cause); } else if(ExceptionC.class.isinstance(cause)){ return ExceptionC.class.cast(cause); } else { return new ExceptionD(cause.getMessage(),cause); } }
You don't need generics here All these exceptions are also parentexceptions, so you can return them When you think about it, you try to get methods to return different types This is impossible because if you have a variable initialized from this method, you need to know what the result is And you know the result will be parentexception, but you can't know which parent exception
The reason behind it is that if your method is not written to return parentexception - it returns t (a subclass) And you can return different types of subclasses instead of the subclasses you want to get
In a simpler example, if we have:
class A {} class B extends A{ }; class C extends A{ }; public <T extends A> T test() { return (T) new B(); }
We can call it with C = test(); We actually try to cast (c) new (b); This is incompatible, but we have masked it and we get exceptions at run time