的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