这是用Java设计的,如果我在Exception
上调用getCause()
,我将得到一个Throwable
对象。
我知道getCause()
只是从Throwable
继承,我知道Throwable
可以是Error
或Exception
,但程序员通常应该只在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);
}
getCause
是Throwable
中定义的方法,只是继承了Exception
中的。Throwable
的原因仅仅是Throwable
(它可以是Exception
或Error
)。
在我看来,有一个方法,比如getCauseAsException
,如果有的话,它只是返回Exception
作为原因异常并不是真的有用。如果你只关心Exception
而不是Error
s,你可以简单地调用getCause()
并检查它是否是Exception
的实例。
首先,如果一个类型有几个子类型,并且它们的行为确实是相同的,那么在父类中定义方法是有意义的。基本上,设计师说的是:"Throwable
可以有一个原因,这也是一个可扔的,你可以得到这个原因。"你可以在Exception
和Error
中这样做,因为它们碰巧都是可扔的。
现在,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实例而为相同的方法提供不同的返回类型。