Java泛型:返回绑定的泛型类型

时间:2018-08-17 08:35:16

标签: java generics

我遵循以下代码:

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);
    }
}

ExceptionAExceptionBExceptionCExceptionDParentException的子代。

编译时出现错误:

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有界并且所有返回的对象都落入指定界时,为什么需要使用类型转换?

3 个答案:

答案 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推断TExceptionA,您的代码将进入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);

对于我可以想象的用例,这应该正是您所需要的。