📑

【Dart】try、catch、throwの使い方メモ

2022/02/08に公開

はじめに

フルゲンと申します。
以下Kboyさんの動画の
try、catch、throwについて学習した内容のメモになります。
YouTubeのvideoIDが不正ですhttps://youtu.be/3gySprP_kw4?t=1187
サンプルなどは動画内のコードを使ってます。
わかりやすく説明されているので、こちらも参考にしてみてください。

どなたかの参考になれば幸いです。
間違い等ございましたらご指摘いただけるとありがたいです。

try、catch、throwについて

try、catch、throw文を使って例外を処理することができる。

try

通常処理を行うコードを入れる。tryで囲むと例外が発生するとcatch(e)のeに例外を入れてくれる。

catch

tryで発生した例外の処理をかく。
(例外インスタンスeの内容表示したり、スタックトレース表示したりして開発者に伝える。)

throw

例外が起きたことを開発者(システム?)に知らせる。
(アプリ上では表示は何もない。)

try、 catch、 throwを使ったバリデーションの具体例

前提

本の一覧を表示するページadd_book_page.dartと、
本の状態を管理するadd_book_model.dartがあるとします。
add_book_page.dartのaddBook()内で、
add_book_model.dart内の本をfirestoreに追加するメソッド
addBookToFirestore()を呼び出して何らかの処理しているとします。

今回はaddBook()内の例外処理をしてみます。

1. 例外を捕まえたい処理をtry のbodyで囲み、例外が起きた時の処理をcatchのbodyでかく。

add_book_page.dart
  Future addBook(AddBookModel model, BuildContext context) async {
    try {
      //うまくいった場合のこと(通常処理)をかく
      await model.addBookToFirebase();  
      ...
    } catch (e) {
      //exceptionが発生した場合のことをかく。 今回はダイアログを出す。
      showDialog(
        ...
      );
    }
  }

2. try文の中で呼び出されるメソッド側でthrowを定義しておく

throwはtry文の中で呼び出されるメソッドのbodyの中でif文などを使って記述しておきます。
以下では「textが空っぽなら例外を投げる」という処理になります。
throwされると、メソッド内のthrow文から下は実行されず
呼び出しもとのcatchに処理は飛びます。

add_book_model.dart
Future addBookToFirebase() async {
    //ここでバリデーションする
    if (bookTitle.isEmpty) {
      throw ('タイトル入力してください。');
    }
    // throwされると、ここから下は実行されず、呼び出し元のcatch文に飛ぶ
    FirebaseFirestore.instance.collection('books').add({
      'title': bookTitle, 
      'createdAt': Timestamp.now() 
    }); 
  }

さらにこの上のthrow()で定義しているtext(今回は''タイトル入力してください。'')
catch(e)eに格納されるので、
e.toStringで呼び出すことができる。
以下実行例

add_book_page.dart
  Future addBook(AddBookModel model, BuildContext context) async {
    try {
      await model.addBookToFirebase();      
    } catch (e) {
      //exceptionが発生した場合のことをかく
      showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            // addBookToFirebase()のthrowで定義した文章を
            // e.toString()を使って表示している。
            title: Text(e.toString()),
            actions: <Widget>[
              ElevatedButton(
                child: Text('OK'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          );
        },
      );
    }
  }

AlertDialogの部分はこんな感じで表示されます。

注意

  • 今回のようにthrowを含んでいるメソッドがFutureを返す場合は
    メソッドを呼び出し側のtryにasync、メソッド呼び出し処理にawaitつける必要がある。
    これをしないと、exceptionはtry内でキャッチされず、メソッドの次の文が実行されてしまいます。

  • 本番のコードではExceptionを自分で定義する方法は推奨されていないとのことです。
    Dartの用意しているFormatExceptionなどを使う方が良いかもしれません。

Creating instances of Exception directly with Exception("message") is discouraged in library code since it doesn't give users a precise type they can catch. It may be reasonable to use instances of this class in tests or during development.
Exception のインスタンスを Exception("message") で直接作成することは、ユーザーにキャッチできる正確な型を与えないため、ライブラリコードでは推奨されません。テストや開発中にこのクラスのインスタンスを使用することは、合理的かもしれません。
Exception class - dart:core library - Dart API

終わりに

ここまで読んでいただきありがとうございました!

参考

YouTubeのvideoIDが不正ですhttps://youtu.be/3gySprP_kw4?t=1187
https://api.dart.dev/stable/2.16.0/dart-core/Exception-class.html

Discussion