的Java不能赶上通用的异常类型,由于擦除
catch(FooException<Bar> e) --analogy-> o instanceof List<String>
catch
和instanceof
依赖于运行时类型信息;擦除废墟。
但是,禁止像FooException<T>
这样的异常类型的泛型声明有点过于苛刻。Java可以允许它;只是要求只有原始类型和通配符类型被允许在catch
catch(FooException e) --analogy-> o instanceof List
catch(FooException<?> e) o instanceof List<?>
的参数可以作出,异常的类型主要是由catch子句兴趣;如果我们无法捕捉到泛型异常类型,那么首先将它们排除在外是没有意义的。好。现在
,我们不能做到这一点无论
catch(X e) // X is a type variable
那么为什么Java的允许X
在首位抛出?
... foo() throws X
正如我们后面会看到的,这个构造在擦除的辅助下实际上可以颠覆类型系统。
嗯,这个功能虽然 - 至少在概念上是非常有用的。我们编写泛型方法,以便我们可以为未知输入和返回类型编写模板代码;相同的投掷类型逻辑!例如,
<X extends Exception> void logAndThrow(X ex) throws X
...
throw ex;
...
(IOException e){
logAndThrow(e); // throws IOException
我们需要能够一般性地在抽象代码块时实现异常透明。另外一个例子,假设我们已经厌倦了写这种样板反复
lock.lock();
try{
CODE
}finally{
lock.unlock();
}
的,我们要为一个包装方法,以在拉姆达的代码
Util.withLock(lock,()->{ CODE });
的问题是,代码可能抛出任何异常类型;原始的样板抛出任何CODE抛出;并且我们希望writeLock()
表达式可以执行相同的操作,即异常透明度。解决方案 -
interface Code<X extends Throwable>
{
void run() throws X;
}
<X extends Throwable> void withLock(Lock lock, Code<X> code) throws X
...
code.run(); // throws X
...
withLock(lock,()->{ throws new FooException(); }); // throws FooException
这只对检查的异常很重要。无论如何,未经检查的异常都可以自由传播。
throws X
方法的一个严重缺陷是它不适用于2个或更多的异常类型。
好吧,那真的很好。但是我们在新的JDK API中没有看到这样的用法。没有任何方法参数的功能类型可以抛出任何检查的异常。如果你这样做Stream.map(x->{ BODY })
,BODY
不能抛出任何检查的异常。他们真的很讨厌用lambdas检查异常。
另一个例子是CompletableFuture
,其中异常是整个概念的中心部分;但是,在lambda体中不允许检查异常。
如果你是一个梦想家,你可能会相信在未来的某个版本的Java中,将会发明一种优雅的lambda异常透明机制,没有像这样丑陋的throws X
黑客;因此我们现在不需要用<X>
s污染我们的API。是的,梦想,男人。
那么,throws X
用了多少呢?
在整个JDK源代码中,唯一公开的API是Optional.orElseThrow()
(及其堂兄OptionalInt/Long/Double
)。而已。
(有与orElseGet/orElseThrow
设计一个缺点 - 分支的决定不能在lambda体内完毕后,我们可以改为设计一个更常用的方法
T orElse(lambda) throws X
optional.orElse(()->{ return x; });
optional.orElse(()->{ throw ex; });
optional.orElse(()->{ if(..)return x; else throw ex; });
此外orElseThrow
,在JDK中唯一的其他方法throws X
是非公ForkJoinTask.uncheckedThrow()
它用于“偷偷摸摸扔”,即代码可以抛出检查异常,而编译器和运行不知情的情况 -
void foo() // no checked exception declared in throws
{
throw sneakyThrow(new IOExcepiton());
}
这里,IOException
在运行时从foo()
丢弃;但编译器和运行时未能阻止它。擦除主要是责备。
public static RuntimeException sneakyThrow(Throwable t)
{
throw Util.<RuntimeException>sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T
{
throw (T)t;
}
不能有通用的'Exception'类型,但可以将'Exception'类型绑定到泛型参数。 –
另外,这不是源代码声明,它是javadoc中的一些混淆(边界)。 –
@SotiriosDelimanolis我已经指出了。 – Mordechai