我遵循以下代码:
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);
}
}
ExceptionA
,ExceptionB
,ExceptionC
,ExceptionD
是ParentException
的子代。
编译时出现错误:
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
但是,如果我将代码更改为:
@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);
}
}
它可以正常工作,没有编译错误。
如SO线程How do I make the method return type generic?的答案中所述,允许使用T
进行强制转换,并且在该线程Java Generics: Generic type defined as return type only中给出了另一个指针。但是我的问题是:当T
有界并且所有返回的对象都落入指定界时,为什么需要使用类型转换?
答案 0 :(得分:5)
您在做什么是错误的。这就是为什么您得到错误。您可以使用ExceptionC exceptionC=managedException(ExceptionD d)
来调用您的方法,最后将进行强制转换(ExceptionC) exceptionD;
并强制转换它掩盖了错误,但您可以在运行时得到它。
将您的方法更改为:
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);
}
}
这里不需要泛型。所有这些异常也是ParentExceptions,因此您可以使用juse返回它们。考虑它时,您试图使该方法返回不同的类型。不能那样做,因为如果您有一个使用此方法初始化的变量,则需要知道结果是什么。而且您知道结果将是ParentException,但是您不知道那是哪种父异常。
其背后的原因是,如果您编写的方法像这样,则不会返回ParentException-它会返回T(子类)。而且您可以返回另一种类型的子类,而不是您尝试获得的子类。
在一个简单的示例中,如果我们有:
class A {}
class B extends A{ };
class C extends A{ };
public <T extends A> T test() {
return (T) new B();
}
我们可以使用C c=test();
来调用它,实际上我们尝试投射不兼容的(C) new B();
,但是我们已经屏蔽了它,并且在运行时得到了异常
答案 1 :(得分:4)
因为
ExceptionA a = managedException(new ExceptionB());
将以ClassCastException
崩溃。 Java推断T
为ExceptionA
,您的代码将进入B
情况,这将导致错误的转换。
Java正确地说ExceptionC
不是T
是因为T
也可以是ParentException
的任何其他子类型。
答案 2 :(得分:2)
好吧,T
在您的方法上下文中是一个动态类型参数,由编译器从调用上下文中推断出来。破坏您的方法很容易:
ExceptionC c = managedException(new ExceptionA());
在两个版本中都将在运行时失败。这就是为什么编译器不接受您的任何版本的原因(第二个版本除外,带有泛型警告,出于我上面概述的原因而有效)。
要绑定参数,通常需要添加一个Class<T>
参数:
public <T extends ParentException> T managedException(Exception cause, Class<T> targetClass);
但是我认为这很明显为什么会导致方法无效(从一个类到另一个不相关的类的强制转换肯定会在运行时失败)。
该方法本身看起来有些怪异。与无操作强制转换唯一不同的是最后的ExceptionD
构造。也许您应该重新考虑您真正想要实现的目标-这种方法似乎不是实现它的正确方法。
另一种解决方案:有时使用泛型是不正确的;-)
为什么不只是写
public ParentException managedException(Exception cause);
对于我可以想象的用例,这应该正是您所需要的。