如果在异常上调用getCause(),为什么必须处理Throwable ?



这是用Java设计的,如果我在Exception上调用getCause(),我将得到一个Throwable对象。

我知道getCause()只是从Throwable继承,我知道Throwable可以是ErrorException,但程序员通常应该只在Exception级别工作,而不处理Throwable/Error类。

Java异常层次结构设计中的原因是什么,例如,不将getCause()包含在将返回Exception对象的Exception类中?

下面是取自Java Concurrency In Practice (Brian Goetz)的不便的例子:

public class Preloader {
    private final FutureTask<ProductInfo> future =
        new FutureTask<ProductInfo>(new Callable<ProductInfo>() {
            public ProductInfo call() throws DataLoadException {
                return loadProductInfo();
            }
        });
    private final Thread thread = new Thread(future);
    public void start() { thread.start(); }
    public ProductInfo get()
        throws DataLoadException, InterruptedException {
        try {
            return future.get();
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof DataLoadException)
                throw (DataLoadException) cause;
            else
                throw launderThrowable(cause);
        }
    }
}

书中说:

…还因为ExecutionException的原因返回为Throwable,不方便处理…

launderThrowable()中,它被处理为立即重新抛出Error(因为我们不想处理它)并返回RuntimeException:

public static RuntimeException launderThrowable(Throwable t) {
    if (t instanceof RuntimeException)
        return (RuntimeException) t;
    else if (t instanceof Error)
        throw (Error) t;
    else
        throw new IllegalStateException("Not unchecked", t);
}

getCauseThrowable中定义的方法,只是继承了Exception中的Throwable的原因仅仅是Throwable(它可以是ExceptionError)。

在我看来,有一个方法,比如getCauseAsException,如果有的话,它只是返回Exception作为原因异常并不是真的有用。如果你只关心Exception而不是Error s,你可以简单地调用getCause()并检查它是否是Exception的实例。

首先,如果一个类型有几个子类型,并且它们的行为确实是相同的,那么在父类中定义方法是有意义的。基本上,设计师说的是:"Throwable可以有一个原因,这也是一个可扔的,你可以得到这个原因。"你可以在ExceptionError中这样做,因为它们碰巧都是可扔的。

现在,Throwable层次结构从Java 1.0开始就存在了,而泛型在那里不存在。现在,您可以这样定义行为:

class ModernThrowable<T extends ModernThrowable<T>> {
    T getCause() {...}
}

您可以将ModernException定义为extends ModernThrowable<ModernException>,然后您可以获得您期望的行为。

但是这个设计在当时不存在,所以你得到了一个Throwable,你必须强制转换它。这就是过去的工作方式,你必须保持向后兼容性。

但实际上……尽管这听起来很合理,但这是不正确的。至少,这不是全部的真相。

Error作为Exception的原因是完全可以的。看看javax.management.RuntimeErrorException。当您使用代理时,您可能会收到Error,在这些特殊情况下,它不应该导致您立即中止系统。所以你把它作为这个特殊例外的原因。因此,您的getCause()实际上将从Exception返回Error

因此,如果它不是这样设计的,你将不得不经历重重困难——为这个特殊异常的原因使用一个专门的方法

程序员在实现应用程序时通常在Exception级别工作。但是请记住,例如JVM也是用Java实现的,在这种情况下,程序员也应该在Throwable/Error级别工作。

所以,你应该在异常层次树的哪个层次上工作的问题,更多的是你正在实现的系统领域的问题,而不是语言设计本身的问题。因此,从语言设计的角度来看,在Throwable类(它是层次结构树的根)中提供getCause方法是完全有意义的。因为Exception类从它的超类继承了这个方法,所以没有必要仅仅因为在某些特定的域中你不/不应该使用Throwable实例而为相同的方法提供不同的返回类型。

最新更新