什麼是例外?例外就是不正常。在程式語言的範疇中,例外就是當有一個事件發生,而且會中斷正常流程。例如,當我們需要取得字串的長度,結果傳進來的字串是空值 (Null),空值表示在記憶體中還沒有設定內容,由不存在的內容取得字串長度是不合法的,故此時會發生例外。
在 Dart 中,如果發生例外卻沒有去處理,那麼就會暫停 Isolate ,且程式被中斷。
Dart 的例外是「未經處理的例外 (Unchecked exception)」:Dart 不會強迫處理每一個會拋出異常函數。與之對立的是 Java:在當呼叫的函數會拋出例外,那麼使用該函數的類或是其子類別,一定要將例外捕捉,
雖然 Dart 不會強迫處理例外,可以讓程式碼乾淨許多,但是函數若發生例外時,程式還是會中斷。
利用 try/catch 來捕捉例外事件。
try-catch 的使用方式如下:將有可能發生例外的程式碼置於 try{ } 裡,當程式碼發生例外時,若與 catch 內預期捕捉到的例外相同,則會執行 catch 裡面的程式碼。其中 catch 裡面的 (error) 為該例外的識別碼 (Identifier),我們可以利用 error 得知詳細的例外訊息。
try{
//Function which may throw an exception
}catch(error){
//Error handle
}
範例:有一個 List 裡面有三個元素,當使用 for 迴圈取值時,將錯誤的索引值帶入此 list 中,那麼就會噴出例外的訊息,程式也因此中斷。
var list = [1,2,3];
for(var i=0; i<=l.length; i++){
print(list[i]);
}
//Unhandled exception:
//RangeError (index): Invalid value: Not in inclusive range 0..2: 3
var list = [1,2,3];
try{
for(var i=0; i<=l.length; i++){
print(list[i]);
}
}catch(error){
print(error);
}
//RangeError (index): Invalid value: Not in inclusive range 0..2: 3
→ 程式不會因為例外而中斷。
在 Dart 中,有眾多預設的例外 (Exception) 以及錯誤 (Error)。
我們可以使用 on Exception_Name catch (identifier) 或是 on Error_Name catch (identifier)來捕捉特定的例外或錯誤。
var list = [1,2,3];
try{
for(var i=0; i<=l.length; i++){
print(list[i]);
}
}on RangeError catch(error){
print(error);
}
例用關鍵字 throw 我們也可以自行拋出例外、錯誤訊息。
範例:有一個函數還沒有實作內容,我們可以拋出 UnimplementedError,當呼叫此函數就會拋出錯誤。
void save(String content) => throw UnimplementedError();
void main(){
save('content');
}
//UnimplementedError
除了可以拋出 Dart 預設的例外、錯誤,我們也可以自行實作例外、錯誤。
範例:
class MyError extends Error{
final String message;
MyError(this.message);
@override
String toString() {
return 'MyError: $message';
}
}
測試:
void main(){
throw MyError('This is my customized error');
}
//Unhandled exception:
//MyError: 'This is my customized error'
使用 try-catch 捕捉例外、錯誤,當例外、錯誤發生時,原本的流程就會被中斷,接者會移動到 catch 中。如果有些事情一定需要處理,不管是正常的流程結束,或是錯誤的流程結束。
例如:有一個發送 log 的系統,當正常時,會把所有收集到的 log 發送出去,若有錯誤發生,則發送錯誤的訊息。
關鍵字 finally 可以接在 try-catch 的最後方,無論程序是否正常或是異常,在 finally 裡面的程式碼一定會被呼叫。
範例:
String log;
try{
log = collectLog();
}catch(error){
log = error;
}finally{
send(log);
}
try-catch-finally 不一定需要三個都使用,我們可以只用其中兩個:try-catch 或是 try-finally,這完全取決於使用情境。
例外處理是每一個程式語言都必須面對的,正向的程式邏輯編寫完成之後,反向的例外處理也必須要考慮。但是因為發生例外之後,原本的程式流程就會跳到別的地方,在處理的時候就要特別的小心。
為什麼 Dart 是未經檢查的例外,原因在於,它不希望我們用了一大堆 try-catch 去捕捉錯誤,只要針對一定要捕捉的去捕捉,因為以前在開發 Java 時,很常遇到某個函數會拋出例外,我們用 try-catch 去捕捉例外,但是在 catch 裡面卻又沒有做任何事情,那麼原本應該發生錯誤通知使用者的,變成沒通知,然後可能帶者錯誤的狀態繼續往下。所以為什麼 Kotlin、Dart 都改成未經檢查的例外。