MENU

【Dart】Nullsafety と? 、! 、??の使い方について

この記事を読むのに必要な時間は約 13 分です。

この記事ではDart におけるNullsafety とその使い方について説明します

・Dart の型はデフォルトでnull を許容しない。
・型の後ろに? を付けることでnull 許容することができるが、メソッド、関数、値の代入時など注意が必要。

目次

Nullsafety とは

①Nullsafety とは

「Nullsafety 」に対応しているとは、デフォルトで型がnull を許容しないという意味です。

※ null とは何もない状態、例えば変数に何も代入されていないような状態を指します。

Dart 2.12 & Flutter 2. 以降ではNullsafety に対応しています。これはAPIでnon-nullが圧倒的に多かったかららしいです。

下記の例ではNullsafety によりint 型はnon-nullable な型 (null を許容しない型)のためエラーが起きます。

void main() {
  int a; // int はnon-nullable な型
  a = null; 
  print('a is $a.');  // エラー
}


エラー内容

A value of type ‘Null’ can’t be assigned to a variable of type ‘int’.
Try changing the type of the variable, or casting the right-hand type to ‘int’.

Null の許容の仕方

①? を使うとnull を許容できる

型の後に? を付けることでnull の代入を許容できます。

下記の例ではint? 型はnull を許容するためエラーになりません。

void main() {
  int? a; // int? はnullable な型
  a = null;
  print('a is $a.');  // console  a is null
}

また、ジェネリクス型でもnullable、non-nullable の使い分けはされます。

ジェネリクスについてはこちらの記事を一読ください。

下記の例ではLIst<String> とString はnon-nulable な型なのでエラーが起きます。

void main() {
  List<String> aListOfStrings = ['one', 'two', 'three'];
  List<String> aNullableListOfStrings; // LIst<String> はnon-nulable な型。
  List<String> aListOfNullableStrings = ['one', null, 'three'];  /// String  はnon-nulable な型。

  print('aListOfStrings is $aListOfStrings.'); // console  aListOfStrings is 'one', 'two', 'three'
  print('aNullableListOfStrings is $aNullableListOfStrings.'); // aNullableListOfStringsに値を代入していないためエラー
  print('aListOfNullableStrings is $aListOfNullableStrings.'); // aListOfNullableStringsの第2引数は null なのでエラー
}

aNullableListOfStrings はList 自体がnull なのでList<String>? とし、

aListOfNullableStrings はList の要素が一部null なのでList <String?> とすることで解決します。

void main() {
  List<String> aListOfStrings = ['one', 'two', 'three'];
  List<String>? aNullableListOfStrings;
  List<String?> aListOfNullableStrings = ['one', null, 'three'];

  print('aListOfStrings is $aListOfStrings.'); 
  print('aNullableListOfStrings is $aNullableListOfStrings.');
  print('aListOfNullableStrings is $aListOfNullableStrings.');
}

Null を許容した際の注意点

Null を許容してエラーになる例

下記の例を見て下さい。

int? couldReturnNullButDoesnt() => -3;

void main() {
  int? couldBeNullButIsnt = 1;
  List<int?> listThatCouldHoldNulls = [2, null, 4];

  int a = couldBeNullButIsnt;
  int b = listThatCouldHoldNulls.first; // first item in the list  エラー
  int c = couldReturnNullButDoesnt().abs(); // absolute value エラー

  print('a is $a.');
  print('b is $b.');
  print('c is $c.');
}

この例ではint? 型のlistThatCouldHoldNulls の一要素目をint 型 b に代入しようとしているため、エラーになります。

また、couldReturnNullButDoesnt についてもnull が入る可能性があるので.abs() が適用できず、c はエラーになります。

このようにnull を許容した変数に対して、「null を渡せないメソッドや関数を使用する」、「null を許容しない変数を代入する」とエラーが起きます。

後述の①、②、③いずれかの対応が必要です。

①null でないことを確定させる

? で定義した変数がnull でないことが確実な場合、null断定演算子null assertion operator (!)を使用しnon-nullableであることを確定させます。

listThatCouldHoldNulls.first! 、couldReturnNullButDoesnt()! とするとnull でないことが確定されるのでエラーは消えます。

int? couldReturnNullButDoesnt() => -3;

void main() {
  int? couldBeNullButIsnt = 1;
  List<int?> listThatCouldHoldNulls = [2, null, 4];

  int a = couldBeNullButIsnt;
  int b = listThatCouldHoldNulls.first!; // first item in the list 
  int c = couldReturnNullButDoesnt()!.abs(); // absolute value

  print('a is $a.');
  print('b is $b.');
  print('c is $c.');
}

ただし、!は例外を投げるため、null であることが明らかな場合以外は使用を避けた方がよさそうです。

Dart の公式ページでも下記のように言及しています。

 If you’re wrong, Dart throws an exception at run-time. This makes the ! operator unsafe, so don’t use it unless you’re very sure that the expression isn’t null.

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

②null だった場合、何を入れるかを ?? で指定する

「nullでない場合の処理 ?? nullの場合の処理 」という形で場合分けの処理ができます。

つまり、下記のようにしてもエラーを回避することが可能です。

int? couldReturnNullButDoesnt() => -3;

void main() {
  int? couldBeNullButIsnt = 1;
  List<int?> listThatCouldHoldNulls = [3, null, 4];

  int a = couldBeNullButIsnt;
  int b = listThatCouldHoldNulls.first ?? 3; // first item in the list
  int c = couldReturnNullButDoesnt() ?? -3.abs(); // absolute value

  print('a is $a.');
  print('b is $b.');
  print('c is $c.');
}

③変数宣言時の型を変える

型を変えても問題ないなら下記のようにしてもエラーを取り除けます。

int couldReturnNullButDoesnt() => -3;

void main() {
  int? couldBeNullButIsnt = 1;
  List<int?> listThatCouldHoldNulls = [2, null, 4];

  int a = couldBeNullButIsnt;
  int? b = listThatCouldHoldNulls.first; // first item in the list
  int c = couldReturnNullButDoesnt().abs(); // absolute value

  print('a is $a.');
  print('b is $b.');
  print('c is $c.');
}

参考サイトの紹介

Codelab でNullsafety のexercise があったのでリンクを貼っておきます。ぜひ手を動かして理解を深めて下さい。

あわせて読みたい
Understanding null safety A deep dive into Dart language and library changes related to null safety.

さらに詳しく知りたい場合はこちらを見てみるのが良さそうです。

あわせて読みたい
Understanding null safety A deep dive into Dart language and library changes related to null safety.

以上、Dart のNullsafety についてまとめてみました!

カテゴリー

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次