【入門編】Null安全下での「Getter/Setter」設計:初期化状態をどうカプセル化するか – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは。Dartの世界へようこそ。
Dartの「Null安全(Sound Null Safety)」は、単なるエラー回避の仕組みではありません。これは「コンパイラがコードの実行時におけるNull参照を数学的に排除する」という、極めて強力な保証です。

今回は、Null安全下での「Getter/Setter」の設計という、一見地味ですが、実はアプリケーションの堅牢性を左右する最重要テーマについてお話しします。

—

1. なぜ「直接の代入」を避けるべきなのか?

他の言語から来た方は、ついつい `public` な変数を公開しがちです。しかし、Dartにおいて「状態」を直接公開することは、Null安全の恩恵を自ら捨てるようなものです。

例えば、こんなコードを見てください。

class UserProfile {
String? nickname; // 外部から直接アクセス可能
}

これでは、どこで `null` が代入されるか制御できません。DartのNull安全は「変数がNullになり得るか」を静的に解析します。`nickname` が Null であることを許容してしまった瞬間、その値を使うすべての場所で「本当にNullじゃない?」というチェック(`! ` や `?.`)を強制され、コードが汚染されてしまいます。

—

2. カプセル化による「状態の守り方」

外部には「値が存在すること」を保証しつつ、内部では状態の変化を制御する。これがプロのGetter/Setter設計です。

基本戦略:Private変数と公開Getter

Dartのプライベート変数(`_` プレフィックス)を使い、外部公開用のGetterを定義します。

class UserProfile {
// 1. 内部状態は private にして外部から隠す
String? _nickname;

// 2. コンストラクタで初期化を強制する
UserProfile(String initialNickname) : _nickname = initialNickname;

// 3. Getter: 外部には「確実に値がある」ことを保証して返す
// Null安全の観点から、もしNullならデフォルト値を返すのが定石
String get nickname => _nickname ?? “名無しの権兵衛”;

// 4. Setter: バリデーションを挟んでNullを排除する
set nickname(String value) {
if (value.trim().isEmpty) {
throw ArgumentError(“ニックネームは空にできません!”);
}
_nickname = value;
}
}

この設計の「魂」

  • Getterの役割: 内部で `null` であっても、外部には「デフォルト値」という救済措置を提示することで、利用側のコードで `!` を使わせない。
  • Setterの役割: 値が代入される入り口で門番(バリデーション)を行い、不正な値や `null` が内部状態に入り込むのを物理的に防ぐ。

—

3. 陥りやすい「Null安全の罠」

初学者がよくやるミスは、「初期化忘れ」です。Dartは「非Null型(`String`など)の変数は、宣言時またはコンストラクタで必ず初期化しなければならない」という厳しいルールがあります。

悪い例:初期化をサボる

class Config {
late String apiKey; // late を使えばコンパイルは通るが…
}

`late` は「後で必ず初期化するから信じてくれ!」というコンパイラへの誓約書です。しかし、初期化する前に `apiKey` にアクセスすると、実行時に `LateInitializationError` が発生し、アプリはクラッシュします。

解決策: コンストラクタで引数として受け取るか、デフォルト値を与える。

class Config {
// コンストラクタで初期化を強制するのが最も安全
final String apiKey;

Config(this.apiKey);
}

—

4. チーフアーキテクトからのアドバイス:DartVMの視点

Dartの素晴らしい点は、「開発時の安全性」と「実行時の最適化」が両立していることです。

Null安全が正しく設計されたコードは、Dart VM(仮想マシン)にとって「この変数は絶対にNullにならない」という強力なヒントになります。これにより、VMは頻繁に行われるNullチェックを省略し、極限まで最適化されたマシンコードを生成します。

つまり、「安全なコードを書くこと」は「高速なアプリを作ること」に直結しているのです。

—

まとめ:ここをマスターすれば大丈夫!

1. フィールドは `_` で隠す: 外部に直接公開しない。
2. Getterで「値がある保証」を作る: `??` 演算子を活用し、呼び出し側にNullを意識させない設計にする。
3. Setterで門番を置く: 不正な値やNullの侵入を許さない。
4. `late` は最後の手段: できる限りコンストラクタで初期化を完結させる。

Null安全は、最初は窮屈に感じるかもしれません。ですが、これは皆さんが書くコードの「品質」を守るための最強の防壁です。この防壁を正しく設計できるようになれば、あなたはもう初心者ではありません。

ぜひ、自分の書くクラスを見直して、「本当にこの変数は外部から書き換えられても安全か?」を自問自答してみてください。それが、Dartを掌握する第一歩です。

また何か詰まったら、いつでも聞きに来てくださいね。一緒に最高の実装を追求しましょう!

タイトルとURLをコピーしました