try 運算式


如果你撰寫了這段程式:
val filename = args(0)

使 用者如果沒有提供命令列引數,則args的長度會是0,存取索引0就會發生超出陣列索引範圍的錯誤,在Scala中,會丟出 java.lang.ArrayIndexOutOfBoundsException。你可以使用isEmpty來測試args是否為空,在沒有提供引數 時建立例外物件丟出以提供錯誤訊息:
if(args.isEmpty)
    throw new IllegalArgumentException("必須提供引數")

val filename = args(0)
// 繼續程式...


另一個方式是使用try...catch語句,在使用者沒有提供引數時,於catch中捕捉例外並顯示錯誤訊息:
try {
    val filename = args(0)
    // 繼續程式...
}
catch {
    case ex: ArrayIndexOutOfBoundsException => println("必須提供引數")
}

程式會嘗試取得args(0),如果丟出例外,而在catch中有撰寫對應的case比對,則會執行對應的處理程式。如果有多個例外類似要比對,則可以撰寫多個case,如果最後有一定得處理的程式,則可以撰寫finally區塊,例如:
import java.io._
try {
    val reader = new FileReader(args(0))
    try {
        // 作一些處理
    }
    catch {
        case ex: IOException => println("發生 IO 錯誤")
    }
    finally {
        reader.close()
    }
}
catch {
    case ex: ArrayIndexOutOfBoundsException => println("必須提供引數")
    case ex: FileNotFoundException => println("找不到檔案:" + args(0))
}

在Scala中,並不要求你得處理受檢例外(Checked Exception)(參考Java的
例 外的繼承架構), 你只需針對感興趣或有能力處理的例外加以捕捉並處理即可,如果在例外發生的當時情境下無力處理,你什麼事都不用作(不用寫try...catch也不用在 函式或方法上用throws宣告),將處理留給後續的呼叫者來善後(在Java中,受檢例外是由編譯器進行檢查,Scala編譯器不檢查受檢例外,事實 上,Scala也沒有throws關鍵字。如果你要讓某個方法在編譯後產生的位元碼中,有Java中throws宣告,以便給Java程式使用時可以得到 受檢例外檢查機制,則可以使用@throws標注,標注的使用之後還會說明)。

在Scala中,try...catch...finally是運算式,所以會有運算結果,例如你可以撰寫這樣的程式碼:
val filename =
    try {
        args(0)
    }
    catch {
        case ex: ArrayIndexOutOfBoundsException => "default.properties"
    }

如 果有提供命令列引數,則將filename設定為所提供的值,否則就是設定為default.properties。你也可以在finally中提供傳回 值,不過並不鼓勵,因為finally中基本上是用來關閉、釋放某些資源或作些善後動作。如果你要在finally中提供傳回值,要注意一下以下兩個程式 片段的不同:
def doSome = try { 1 } finally { 2 }

如果你呼叫doSome(),則以上程式片段會傳回try區塊中的1,但如果是:
def doSome: Int = try { return 1 } finally { return 2 }

因 為你明確地使用return,這個程式片段會傳回2(這個行為與Java是相同的)。如先前所提到的,在finally中並不建議有傳回值,而僅用於關 閉、釋放資源或作些善後動作(明確地使用return在Scala中也是不鼓勵的,因為容易破壞程式的結構,在Scala中使用return就無法使用類 型推斷,像以上定義函式時,就得明確定義函式的傳回值型態,另外,return在Scala中只能用於函式之中)。

在Scala中處理例外,要注意一下例外比對的順序,例如:
val filename =
    try {
        args(0)
    }
    catch {
        case ex: Exception => // 作一些處理
        case ex: ArrayIndexOutOfBoundsException => // 作一些處理
    }

由於Exception是ArrayIndexOutOfBoundsException的父類別,所以這個程式片段,只會符到到第一個Exception,第二個ArrayIndexOutOfBoundsException永遠不會被比對到,Scala不會提出警訊(Java的編譯器會對這部份作檢查),程式會照常執行。