官术网_书友最值得收藏!

7.4 用try表達式實現(xiàn)異常處理

Scala的異常處理與其他語言類似。除了正常地返回某個值,方法也可以通過拋出異常來終止執(zhí)行。方法的調(diào)用方要么捕獲并處理這個異常,要么自我終止,讓異常傳播給更上層的調(diào)用方。異常通過這種方式傳播,逐個展開調(diào)用棧,直到某個方法處理了該異常或者沒有更多方法了為止。

拋出異常

在Scala中拋出異常與在Java中拋出異常看上去一樣。你需要創(chuàng)建一個異常對象,然后用throw關(guān)鍵字將它拋出:

雖然看上去有些自相矛盾,但是在Scala中,throw是一個有結(jié)果類型的表達式。下面是一個帶有結(jié)果類型的示例:

在這段代碼中,如果n是偶數(shù),half將被初始化成n的一半。如果n不是偶數(shù),則在half被初始化之前,就會有異常被拋出。因此,我們可以安全地將拋出異常當作任何類型的值來對待。任何想要使用throw給出返回值的上下文都沒有機會真正使用它,也就不必擔心有其他問題。

從技術(shù)上講,拋出異常這個表達式的類型是Nothing。即使表達式從不實際被求值,也可以用throw。這個技術(shù)細節(jié)聽上去有些奇怪,不過在這樣的場景下,還是很常見且很有用的。if表達式的一個分支用于計算出某個值,而另一個分支用于拋出異常并計算出Nothing。整個if表達式的類型就是那個計算出某個值的分支的類型。我們將在17.3節(jié)對Nothing做進一步的介紹。

捕獲異常

可以用示例7.11中的語法來捕獲異常。catch子句的語法之所以是這樣的,是為了與Scala的一個重要組成部分,即模式匹配pattern matching),保持一致。我們將在本章簡單介紹并在第13章詳細介紹模式匹配這個強大的功能。

這個try-catch表達式與其他帶有異常處理功能的語言一樣。首先,代碼體會被執(zhí)行,如果拋出異常,則會依次嘗試每個catch子句。在本例中,如果異常類型是FileNotFoundException,則第一個子句將被執(zhí)行;如果異常類型是IOException,則第二個子句將被執(zhí)行;如果異常既不是FileNotFoundException也不是IOException,則try-catch將會終止,并將異常向上繼續(xù)傳播。

示例7.11 Scala中的try-catch子句

注意

你會注意到一個Scala與Java的區(qū)別,Scala并不要求捕獲受檢異常checked exception)或在throws子句里聲明。可以使用@throws注解聲明一個throws子句,但這并不是必需的。關(guān)于@throws注解的詳情,請參考9.2節(jié)。

finally子句

可以將那些無論是否拋出異常都需要執(zhí)行的代碼以表達式的形式包含在finally子句中。例如,你可能想要確保某個打開的文件被正確關(guān)閉,即使某個方法因為拋出了異常而退出。示例7.12給出了這樣的例子:[5]

示例7.12 Scala中的try-finally子句

注意

示例7.12展示了確保非內(nèi)存資源被正確關(guān)閉的慣用做法。這些資源可以是文件、套接字、數(shù)據(jù)庫連接等。首先獲取資源,然后在try代碼塊中使用資源,最后在finally代碼塊中關(guān)閉資源。關(guān)于這個習慣,Scala和Java是一致的。Scala提供了另一種技巧,即貸出模式loan pattern),可以更精簡地達到相同的目的。我們將在9.4節(jié)詳細介紹貸出模式。

交出值

與Scala的大多數(shù)其他控制結(jié)構(gòu)一樣,try-catch-finally最終返回一個值。例如,示例7.13展示了如何實現(xiàn)解析URL,但當URL格式有問題時返回一個默認的值。如果沒有異常拋出,整個表達式的結(jié)果就是try子句的結(jié)果;如果有異常拋出且被捕獲,整個表達式的結(jié)果就是對應的catch子句的結(jié)果;而如果有異常拋出但沒有被捕獲,整個表達式就沒有結(jié)果。如果有finally子句,則該子句計算出來的值會被丟棄。finally子句一般都用于執(zhí)行清理工作,如關(guān)閉文件。通常來說,它不應該改變主代碼體或catch子句中計算出來的值。

示例7.13 交出值的catch語句

如果你熟悉Java,則需要注意的是,Scala的行為與Java的行為不同,僅僅是因為Java的try-finally子句并不返回某個值。與Java一樣,當finally子句包含一個顯式的返回語句,或者拋出某個異常時,這個返回值或異常將會“改寫”(overrule)任何在之前的try代碼塊或某個catch子句中產(chǎn)生的值。例如,在下面這個函數(shù)定義中:

調(diào)用f()將得到2。相反,如果是如下代碼:

調(diào)用g()將得到1。這兩個函數(shù)的行為都很可能讓多數(shù)程序員感到意外,因此,最好避免在finally子句中返回值,最好將finally子句用來確保某些副作用發(fā)生,如關(guān)閉一個打開的文件。

主站蜘蛛池模板: 浑源县| 苍梧县| 达拉特旗| 屯昌县| 英德市| 尚义县| 广西| 文昌市| 宜兰县| 越西县| 利辛县| 廊坊市| 景泰县| 胶州市| 池州市| 格尔木市| 朔州市| 辽宁省| 内黄县| 望江县| 嵊州市| 安吉县| 尼勒克县| 都兰县| 桂平市| 邵阳县| 孝感市| 平阴县| 南京市| 灵石县| 濮阳市| 鹤山市| 白玉县| 焉耆| 思茅市| 怀安县| 沙坪坝区| 邳州市| 桃江县| 富宁县| 黄骅市|