繁体   English   中英

Java泛型:返回有界泛型类型

[英]Java Generics: Returning Bounded Generic Type

我遵循这段代码:

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线程的答案中所提到的: 如何使方法返回类型通用? 允许使用T转换,并在此线程中给出另一个指针: Java Generics:仅定义为返回类型的泛型类型 但我的问题是:当T有界并且所有返回的对象都落入指定的边界时,为什么我需要使用类型转换?

你在做什么是错的。 这就是你得到错误的原因。 您可以使用ExceptionC exceptionC=managedException(ExceptionD d)调用您的方法,最终会得到一个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,因此您可以返回它们。 当你考虑它时,你试图让方法返回不同的类型。 这样做是不可能的,因为如果你有一个从这个方法初始化的变量,你需要知道结果是什么。 而且您知道结果将是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 c=test(); 我们实际上试图施放(C) new B(); 这是不兼容的,但我们已经屏蔽它,我们在运行时获得异常

因为

ExceptionA a = managedException(new ExceptionB());

将崩溃与ClassCastException Java推断TExceptionA ,你的代码将进入B案例,这将导致错误的演员表。

Java是正确的,因为ExceptionC不是T因为T也可以是ParentException 任何其他子类型。

嗯,在您的方法的上下文中, 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);

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM