Dartのnullについてのまとめ

Dartのnullの扱い方を何度も調べている気がするので、まとめてみたいと思います。
公式ドキュメントから抜き出し・意訳・追記しています。（訳に誤りがありましたら申し訳ありません。）

## Dartのnullのコンセプト
・堅固なnull Safetyにするために、Dartの変数は、デフォルトで ‘non-nullable’ になっています。この時、変数には、宣言された型の値のみをセットでき、nullはセットできません。 (e.g. int i=42)
・変数をnullableであると明示した時だけ、nullと宣言された型の値の両方をセットできるようになります。(e.g. int? i)
・堅固なnull Safetyによって、ランタイムエラーを引き起こす可能性のある箇所を、コーディング時に気がつくことができるエラーに変えます。このおかげで、アプリをデプロイする前に、これらのエラーを修正しておくことができます。

https://dart.dev/null-safety

(「堅固なnull Safety」は、原文では、"Sound null Safety" となっています。"Sound" は形容詞で、「健全な、堅固な」という意味があるんですね。知りませんでした。）



# 復習

なんと公式ドキュメントに、解説と練習問題のページがあります。なんて親切なのでしょう。

https://dart.dev/codelabs/null-safety

## ? → null許容型であることを明示する
型の後ろに「?」をつけると、null許容型(nullable)になります。逆にいうと、「?」がついていない時は、nullは入れられません。

Listの時は、以下のような扱いになるようです。
```dart
  //List自体がnullable
  List<String>? aNullableListOfStrings;
  //Listの要素がnullable
  List<String?> aListOfNullableStrings = ['one', null, 'three'];
```

## ! → null許容型を‘non-nullable’と同じ扱いにする
nullableな型で、nullでないのが確かな場合、変数や関数の後ろに!をつけることで、‘non-nullable’と同じように扱うことができます。

※nullになる可能性のあるところで「！」をつけてしまうと、ランタイムエラーを生んでしまうので、nullでないことを  **"you are very sure"** な時だけ使ってください。とのこと。

例（変数の場合）：
```dart
 List<int?> listThatCouldHoldNulls = [2, null, 4];
//一つ目の要素はnullでないので、「!」をつけてintに代入する（つけないとコンパイルエラーになる）
  int b = listThatCouldHoldNulls.first!; // first item in the list
```
例（関数の場合）：
```dart
int? couldReturnNullButDoesnt() => -3;
//nullは返さないので、「!」をつけて、その後に関数を使える
int c = couldReturnNullButDoesnt()!.abs(); // absolute value
```

## ?. → nullではない時だけ実行する
nullが入っているかもしれない値を扱う時に、「?.」を使うと、その後の式は、nullでない場合だけに実行されます。

例：
```dart
//"nullableString" がnullでない時は、lengthの値を返す。nullの場合はnullを返す
nullableString?.length;
```

## ?? → nullだったら違う値をセットする
例：

```dart
///notnullValueがnullでない場合に、notnullValueを、nullの場合nullValueをセットする
    String value = notnullValue ?? nullValue;
```

## type promotion → nullでないことの自動判定
以下のような場合、Dartはnullableの変数の値がnullでないことを検知して、‘non-nullable’の型と同じように扱えるようになります。これを、"type promotion" といいます。

・利用箇所より上で、値が必ずセットされている場合
・nullチェックがされていて、nullの場合にreturnやthrow Exceptionになっている場合

例：
```dart
int getLength(String? str) {
  // nullの場合は０を返す
  if(str==null){
      return 0;
   }
  //本来　"str" はnullableなので、.lengthをつけたらコンパイルエラーになるが、
  //上でnullチェックしてreturnしていることにより、エラーにならない。
  return str.length;
}
```

## late → 後で値を設定する
変数宣言の前に「late」をつけることで、Dartに以下のことを伝えます。
・まだ値をセットしていない。
・後で値をセットする予定
・変数が使われる前に、値をセットすることを確信している

late で宣言した変数を使う前に値をセットしていないと、エラーが発生します。
※メモ：ただ、試したみたところ、このエラーは、コンパイルエラーではなく実行時エラーのようでした。

例：
```dart
class Meal {
//lateで宣言
  late String _description;

  set description(String desc) {
    _description = 'Meal description: $desc';
  }

  String get description => _description;
}

void main() {
  final myMeal = Meal();
  myMeal.description = 'Feijoada!';//この行をコメントアウトすると、実行時エラー
  print(myMeal.description);
}
```

特別な使い方として、初期値が設定されている変数に「late」をつけると、その変数が使われる時に、初めて初期値が設定されます。初期値設定のコストが高く、必ずしも必要にならない時に便利です。

例：
```dart
class CachedValueProvider {
  late final _cache = _computeValue();//初期値設定しているが、lateをつけている。
  int get value => _cache;
}

void main() {
  print('Calling constructor...');
  var provider = CachedValueProvider();
  print('Getting value...');
  print('The value is ${provider.value}!'); //ここで初めて初期値の設定がよばれる
}
```


## 感想
曖昧な理解のままに、場当たり的に？をつけたりしてしまっていましたが、ようやくすっきり理解できた気がします。Dartさんのnull Safetyに対するこだわりも伝わってきました。Dartの公式ドキュメントはとても親切であることがわかったので、もっと活用していきたいです。

## 情報源
https://dart.dev/null-safety

https://dart.dev/codelabs/null-safety

https://dart.dev/null-safety/understanding-null-safety#lazy-initialization