1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Dart] ロジック付きでメンバを初期化する方法

Last updated at Posted at 2024-07-09

こんにちは。

皆さんはインスタンスの初期化時に、ロジック込みでメンバを初期化したいこと、ありませんか。
色々やり方はあると思います。
Getter で妥協するとか、late を使うとか、 Initializer list を使うとか、諦めるとか。

これらの方法を順に紹介します。
"しつこく挨拶をするクラス"を用いながら説明していきます。

1. Getter で妥協する

ランダムな回数(最大 5 回)挨拶するクラスを生成しました。

class AnnoyingGreet {
  String get greetingText => "Hello!" * Random().nextInt(6);
}

void main() {
  test('Check the behavior of AnnoyingGreet', () {
    final greet = AnnoyingGreet();
    print(greet.greetingText);
    print(greet.greetingText);
    print(greet.greetingText);
  });
}

出力は

Hello!
Hello!Hello!
Hello!Hello!Hello!Hello!

見ての通り、 greetingText が参照されるたびに Getter の処理が評価されています。
初期化時には評価されず、呼ばれたタイミングで"都度"処理が走ります。
※ Getter に重たい処理を書いて高頻度で呼ばれちゃうとパフォーマンスに問題が出るのでお気を付けください

2. late を使う

この方法が一般的によく使われていそうですね。

class AnnoyingGreet {
  late String greetingText;

  AnnoyingGreet() {
    greetingText = "Hello!" * Random().nextInt(6);
  }
}

void main() {
  test('Check the behavior of AnnoyingGreet', () {
    final greet = AnnoyingGreet();
    print(greet.greetingText);
    print(greet.greetingText);
    print(greet.greetingText);
  });
}

シンプルで良いと思います。ただ、一個欠点があって、書き換えが可能です。

final を付けることはできますが、ランタイムエラーでした。できればコンパイルエラーを吐いて欲しいですね、危なかしいです。

class AnnoyingGreet {
  late final String greetingText;

  AnnoyingGreet() {
    greetingText = "Hello!" * Random().nextInt(6);
  }
}

void main() {
  test('Check the behavior of AnnoyingGreet', () {
    final greet = AnnoyingGreet();
    print(greet.greetingText);
    print(greet.greetingText);
    print(greet.greetingText);
    greet.greetingText = "ssss";
    print(greet.greetingText);
  });
}

エラー内容

dart:_internal               LateError._throwFieldAlreadyInitialized
test/widget_test.dart        AnnoyingGreet.greetingText=
test/widget_test.dart 26:11  main.<fn>

LateInitializationError: Field 'greetingText' has already been initialized.

3. Initializer list を用いる

Initilizer list というのは、インスタンス生成時に final なメンバを初期化できる機能です。

class AnnoyingGreet {
  final String greetingText;

  AnnoyingGreet() : greetingText = "Hello!" * Random().nextInt(6);
}

void main() {
  test('Check the behavior of AnnoyingGreet', () {
    final greet = AnnoyingGreet();
    print(greet.greetingText);
    print(greet.greetingText);
    print(greet.greetingText);
    greet.greetingText = "ssss";
    print(greet.greetingText);
  });
}

エラー内容

test/widget_test.dart:24:11: Error: The setter 'greetingText' isn't defined for the class 'AnnoyingGreet'.
 - 'AnnoyingGreet' is from 'test/widget_test.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'greetingText'.
    greet.greetingText = "ssss";
          ^^^^^^^^^^^^

この書き方はとても良いですね。
コンパイル時にエラーを吐いてくれるので安心です。late final はランタイム時でした。
ただし、一つ欠点があって、あまり冗長なロジックを書けないという点です。
例えば、この挨拶をしまくる人にふと「そうだ、挨拶ついでに今日の日付も教えてあげよう」と、斜め上すぎる親切心が芽生えたとします。
↓こんな感じでいけるのでは、と思いましたが、ダメでした。

class AnnoyingGreet {
  final String greetingText;

  AnnoyingGreet() : greetingText = _greetingText();

  String _greetingText() {
    final now = DateTime.now();
    return "${"Hello!" * Random().nextInt(6)} By the way, today is ${now.year}/${now.month}/${now.day}";
  }
}

エラー内容

The instance member '_greetingText' can't be accessed in an initializer. (Documentation)  Try replacing the reference to the instance member with a different expression

Documentation があるようなので見てみましたが、 static をつけるのが一般的な修正方法だそうです。

class AnnoyingGreet {
  final String greetingText;

  AnnoyingGreet() : greetingText = _greetingText();

  static String _greetingText() {
    final now = DateTime.now();
    return "${"Hello!" * Random().nextInt(6)} By the way, today is ${now.year}/${now.month}/${now.day}";
  }
}

void main() {
  test('Check the behavior of AnnoyingGreet', () {
    final greet = AnnoyingGreet();
    print(greet.greetingText);
    print(greet.greetingText);
    print(greet.greetingText);
  });
}

Hello!Hello!Hello!Hello!Hello! By the way, today is 2024/7/10
Hello!Hello!Hello!Hello!Hello! By the way, today is 2024/7/10
Hello!Hello!Hello!Hello!Hello! By the way, today is 2024/7/10

評価されるのは初期化時の一回のみで、書き換えようとしたらコンパイルエラーが出てくれます。
Getter を使った場合はコールされるたびに評価されてしまうという欠点がありましたが、この方法であればクリアできます。
また、 late final を使った場合は、書き換えたらランタイムエラーのみでコンパイルが通ってしまいましたが、この方法ならコンパイルエラーとなります。

ちなみにメンバにアクセスしたいときは static メソッドの引数に渡せばいけます。

class AnnoyingGreet {
  final String greetingText;
  final String member;

  AnnoyingGreet({
    required this.member,
  }) : greetingText = _greetingText(member);

  static String _greetingText(String member) {
    return "Hello! $member";
  }
}

void main() {
  test('Check the behavior of AnnoyingGreet', () {
    final greet = AnnoyingGreet(member: "Takeshi");
    print(greet.greetingText);
    print(greet.greetingText);
    print(greet.greetingText);
  });
}
Hello! Takeshi
Hello! Takeshi
Hello! Takeshi

4. 諦める

パソコンを閉じましょう。

それでは。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?