アサーション
コードを開発する際に想像もしていなかったバグに遭遇することがあります。このようなときに活躍するのがアサーションです。アサーションは、プログラムのある時点で満たされるべき条件を記述し、満たされていない場合は例外を発生させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void main(){ //..年齢計算用の良いコード例 int age1 = 50; checkAge(age1); //..年齢計算用の良いコード例 //..年齢計算用の悪い(112歳以上)コード例 int age2 = 150; checkAge(age2); //..年齢計算用の悪い(112歳以上)コード例 } void checkAge(int age){ assert(age < 112, "bad age ${age}"); } |
アサーション&モード(Flutter)
Dartのコードを開発する際、想定通りに動いているかをチェックするためにアサーションを入れられます。その後ほとんどバグがなければ、同じコードをアサーションなしで実行することができます。
デバッグモードでFlutterを開発しても、ほぼ同様のことができます。
エラー&例外
なぜエラーや例外に対処するか
多くのソフトウェアシステムは、複雑で、何人かが協力して書いています。
複数のソースによる複雑さ:
- ビジネスドメイン
- ソフトウェアを書くこと
- 複数の人が一緒に働き、それぞれが違う観点を持っていること
- など
この複雑さが誤解を生み、エラーや例外につながるのです。コードによってこのエラーが扱えれば、それは大きな問題ではありません。
- もしエラーや例外に対処しなかったら、予測不能の事態が起きた時にユーザはエラーの起きた場所を知る手段がなく、苦しむかもしれません。
- もしエラーや例外に対処していれば、ユーザはエラーを知ることなくプログラムを使い続け、開発者が問題を見つけて直すことができます。
良いエラーや例外への対処は、技術的な専門用語でエンドユーザーをけむに巻くべきではありませんが、問題を追跡する開発者には十分な情報を与えなければなりません。
Dartプログラムを実行しているときに問題があれば、エラーや例外を投げることができます。エラーや例外が起きたとき通常のプログラムは停止し、異常終了します。
エラー
エラーは捕まえることも扱うこともできない深刻な問題です。
例
- RageError : リストを検索する際に、無効なインデックスを使用しようとしている場合
- OutOfMemoryError
例外
例外は、捕まえることも扱うこともできる、比較的深刻ではない問題です。
例
- FormatException : 文字列の構文解析ができません
エラーの対処
エラーを修正することは不可能です。ではどのようにすれば少しでも有効なものになるでしょうか?
そこで行う最善策は、どこで、何が起こったか、など起こったことを記録することです。
例外の対処
アプリケーションが急に停止しないために、例外に対処する必要があります。そのために有効なのが、「try..catch..finally」です。finallyはなくても構いません。
Finally
Dartは、例外が出るかどうかにかかわらず、常にFinallyのブロックを実行します。
1 2 3 4 5 6 7 8 9 10 11 |
void main(){ try{ //ここにやりたいことを書く }catch(e){ //例外を出力 print(e); }finally{ //常に実行される print('I will always be excuted!'); } } |
例外のCatch
まず、例外のCatchについてです。
例
次のコードは、例外をCatchし、それを出力します。
1 2 3 4 5 6 7 8 9 |
void main(){ print('start'); try{ int.parse("mark"); }catch(ex){ print(ex); } print('finish'); } |
1 2 3 |
start FormatException: mark finish |
例外のCatchとスタックトレース
Catchについて、次はスタックトレースです。
例
次のコードは、例外とスタックトレースを取得し、スタックトレースを出力します。
1 2 3 4 5 6 7 8 9 10 |
void main(){ print('start'); try{ int.parse("mark"); }catch(ex, stacktrace){ //スタックトレースを出力 print(stacktrace); } print('finish'); } |
1 2 3 4 5 6 7 8 9 10 11 12 |
start FormatException: mark at Object.wrapException (<anonymous>:357:17) at Object.int_parse (<anonymous>:1316:15) at Object.main (<anonymous>:1449:11) at <anonymous>:1932:9 at <anonymous>:1916:7 at dartProgram (<anonymous>:1927:5) at <anonymous>:1934:3 at replaceJavaScript (https://dartpad.dartlang.org/scripts/frame_dark.html:38:27) at messageHandler (https://dartpad.dartlang.org/scripts/frame_dark.html:54:17) finish</anonymous></anonymous></anonymous></anonymous></anonymous></anonymous></anonymous> |
特定の例外のCatch
特定の例外をCatchしたい場合には、「catch」の代わりに「on」を使います。そのあとに「catch」で、それ以外の例外をCatchすることもできます。
「catch(e)」を使うか、「catch(e, s)」を使うかは、スタックトレースを使うかによって分けてください。
1 2 3 4 5 6 7 8 9 10 11 |
void main(){ print('start'); try{ int.parse("mark"); }on FormatException{ print('invalid string'); }catch(ex, stacktrace){ print(stacktrace); } print('finish'); } |
1 2 3 |
start invalid string finish |
例外のThrow
例外を投げるには「throw」というキーワードを用います。
1 |
throw new TooOldForServiceException(); |
例外のRethrow
例外をcatchした時に、それを次のレベルに投げる(rethrow)することができます。つまり、例外をcatchしてログを残し、その後また例外を投げてより高レベルで対処することができるのです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void misbehave(){ try{ dynamic foo = true; print(foo++); //runtime error }catch(e){ print('misbehave() partially handled ${e.runtimeType}.'); rethrow;//呼び出しもとで例外を見れるようにする } } void main(){ try{ misbehave(); }catch(e){ print('main() finished handling ${e.runtimeType}.'); } } |
1 2 |
misbehave() partially handled JsNoSuchMethodError. main() finished handling JsNoSuchMethodError. |
例外を作る
自分でカスタムの例外を作るのはとても簡単です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
//例外の作成 class TooOldForServiceException implements Exception{ Cadet _cadet; TooOldForServiceException(this._cadet); toString(){ return "${_cadet.name} is too old to be in military service."; } } class Cadet{ String _name; int _age; Cadet(this._name, this._age); get age{ return _age; } get name{ return _name; } } void main(){ print('start'); List<Cadet> cadetList = [ Cadet("Tom", 21), Cadet("Dick", 37), Cadet("Harry", 51), Cadet("Mark", 52), ]; List<Cadet> validCadetList = []; for (Cadet cadet in cadetList){ try{ validateCadet(cadet); validCadetList.add(cadet); }on TooOldForServiceException catch(ex){ print(ex); } } print('finish: ${validCadetList.length} of ${cadetList.length} cadets are valid'); } void validateCadet(Cadet cadet){ //条件を付けて例外を投げます if(cadet.age > 50){ throw new TooOldForServiceException(cadet); } } |
1 2 3 4 |
start Harry is too old to be in military service. Mark is too old to be in military service. finish: 2 of 4 cadets are valid |
コンソールアウトプット
Dartでは、「print」コマンドを使ったときにコンソールが使われます。以下のことを覚えていてください。
- printするときは、変数を「toString()」して文字列に変換します。
- 出力のフォーマットには、文字列と特殊文字が使えます。
1 2 3 4 5 6 7 8 |
void main(){ int oneVriable = 12; String anotherVariable = 'some text'; print('noneVariable: $oneVriable \n\nanotherVariable: \'$anotherVariable\''); } |
1 2 3 |
noneVariable: 12 anotherVariable: 'some text' |