こんにちは。
皆さんはインスタンスの初期化時に、ロジック込みでメンバを初期化したいこと、ありませんか。
色々やり方はあると思います。
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. 諦める
パソコンを閉じましょう。
それでは。