うちのいぬ Tech Blog

Tech Blog of Uchinoinu/My dog

例外処理(Exceptions)の使い方

プログラムを書く上でよくある話ですが、 エラーハンドリングをちゃんとしましょう という話になります。

エラーハンドリングというのはいろいろなやり方があると思いますが、そのうちの一つが try catch による例外処理になるかと思います。

ここれでは effective-dart を参考に、dartでの例外処理の使い方をご紹介します。

dart.dev

例外(Exceptions)

例外オブジェクト(Exception)は dart:core ライブラリに含まれています。

api.dart.dev

dartにはErrorとExceptionがあります。それぞれのだいたいの違いは以下の様になります。

  • Error: プログラムの問題。コード修正で対応できる。
  • Exception: プログラムの問題ではない。実行中に異常が起こった場合のため、コード修正で必ず対応可能とは限らない。

例外を投げます

throw 使用して例外オブジェクトを投げます。

throw Exception('例外です');

例外を補足する

try...catch ステートメントで例外オブジェクトをキャッチします。

  try {
    throw Exception('例外を起こしてみました');
  } on Exception catch (err, stacktrace) {
    print(err);
    print(stacktrace);
    rethrow;
  } finally {
    print('例外処理を終了します');
  }

出力

exception: Exception: 例外を起こしてみました,
#0      hoge (hoge.dart:12:5)
#1      main (hoge.dart:7:3)
#2      new Future.<anonymous closure> (dart:async/future.dart:174:37)
#3      _rootRun (dart:async/zone.dart:1346:47)
#4      _CustomZone.run (dart:async/zone.dart:1258:19)
#5      _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
#6      _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23)
#7      _rootRun (dart:async/zone.dart:1354:13)
#8      _CustomZone.run (dart:async/zone.dart:1258:19)
#9      _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1186:23)
#10     Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#11     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
#12     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
#13     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

例外処理を終了します

試してみましょう

  • これらの挙動を試すには dartpadを使うのがとても便利です。

dartpad.dartlang.org

注意点

catchには on節を利用しましょう

  • 何をcatchしようとしているのか明示的・限定的にすることで、予期しない挙動・エラーを防ぐことができます
try {
  doSomethingBad()
}
on Exception catch(e) {
  handleException(e);
}

Errorは実行前に分析対応できるため、コードでは補足しない

  • 見出しの通りですが、Errorについては、実行前に補足・対応が可能です。
  • このため実行時にErrorを明示的に補足するコードを書く必要性はほぼないということです。

dart-lang.github.io

  • ただし、プログラムの問題で修正が必要な場合には、Errorオブジェクトをthrowすることが effective-dartでは推奨されています
    • throw Error('エラーなのでプログラムを修正してください'); しなさいということですね。

rethrow

dart-lang.github.io

Exceptionクラスをカスタマイズする

  • Flutterでライブラリを利用した開発をしていると、HogeHogeExceptionという例外クラスを目にすることがありますが、これらはExceptionクラスを拡張して実装しているクラスの場合もあると思います
  • 開発での必要に応じてこういったクラスはよきように作っていくと良いかもしれません。
class HogeHogeException implements Exception {
  const HogeHogeException(this.message);

  final String message;

  // toString()をoverrideすることで、このクラス利用時に `print(e)` した際に `Instance of 'HogeHogeException'` ではなく中身の文字を出力することができます。
  @override
  String toString() => message;

  void sendDataToLogStorage(){
    // logic
  }
}

void main() {
  try {
    throw HogeHogeException('HogeHogeExceptionです');
  } on HogeHogeException catch (exception, stacktrace) {
    print(exception);
    print(stacktrace);
    exception.sendDataToLogStorage();
  } finally {
    print('Finally');
  }
}