変数
変数の作成と初期化の例です:
var name = 'Bob';
変数は参照を格納する。name
という変数には、"Bob"という値を持つString
オブジェクトへの参照が格納されている。
name
変数の型はString
と推測されるが、型を指定することで変更できる。オブジェクトの型が1つに限定されない場合は、Object
型(必要に応じてdynamic
型)を指定する。
Object name = 'Bob';
もう一つの方法は、推論される型を明示的に宣言することである:
String name = 'Bob';
Note
このページでは、ローカル変数には型アノテーションではなくvar
を使うというスタイルガイドの推奨に従っている。
Null safety
Dart言語では、ヌルの安全性を確保している。
ヌル・セーフティは、null
に設定された変数への意図しないアクセスによって発生するエラーを防ぎます。このエラーはヌル再参照エラーと呼ばれます。NULL参照解除エラーは、null
と評価される式のプロパティにアクセスしたりメソッドを呼び出したりすると発生します。このルールの例外は、toString()
やhashCode
のように、null
がプロパティやメソッドをサポートしている場合です。ヌル・セーフティでは、Dart コンパイラがコンパイル時にこれらの潜在的なエラーを検出します。
例えば、int
型変数i
の絶対値を求めたいとします。i
がnull
の場合、i.abs()
を呼び出すとnull
の再参照エラーが発生します。他の言語ではこれを試すと実行時エラーになる可能性がありますが、Dartのコンパイラはこのような動作を禁止しています。したがって、Dartアプリが実行時エラーを引き起こすことはありません。
Nullセーフティは、3つの重要な変更をもたらします:
- 変数、パラメータ、その他の関連コンポーネントに型を指定するとき、その型が
null
を許すかどうかを制御することができます。 nullabilityを有効にするには、型宣言の最後に?
を追加する。
String? name // Null可能な型。nullまたは文字列。
String name // NULL不可の型。nullにはできないが、文字列にはできる。
変数を使用する前に初期化する必要があります。Nullable 変数のデフォルトは
null
なので、デフォルトで初期化されます。Dart は null値を持たない型に初期値を設定しません。初期値を設定するように強制されます。Dartでは、初期化されていない変数を観察することはできません。このため、受信者の型がnull
であっても、null
が使用されるメソッドやプロパティをサポートしていない場合、プロパティにアクセスしたりメソッドを呼び出したりすることができません。null可能な型を持つ式のプロパティにアクセスしたり、メソッドを呼び出したりすることはできません。同じ例外が、
hashCode
やtoString()
のようにnull
がサポートするプロパティやメソッドにも適用されます。
健全なヌル・セーフティは、潜在的な 実行時エラー を 編集時 の解析エラーに変える。ヌル・セーフティは、ヌルでない変数にフラグを立てる:
- null以外の値で初期化されていない。
null
値を代入。
このチェックにより、アプリをデプロイする前にこれらのエラーを修正することができる。
デフォルト値
null可能な型を持つ初期化されていない変数の初期値はnullです。数値型の変数も初期値は NULL です。Dart の他のすべてのものと同様、数値もオブジェクトだからです。
int? lineCount;
assert(lineCount == null);
Note
本番コードは assert()
呼び出しを無視する。一方、開発中のコードでは、assert(condition)
は条件が false の場合に例外をスローします。詳細は Assertを参照ください。
ヌル・セーフティでは、ヌルでない変数の値は、使用する前に初期化しなければならない:
int lineCount = 0;
ローカル変数は宣言された場所で初期化する必要はありませんが、使用する前に値を代入する必要があります。例えば、以下のコードは、lineCount
がprint()
に渡されるまでにNULLでないことをDartが検出できるため、有効です:
int lineCount;
if (weLikeToCount) {
lineCount = countLines();
} else {
lineCount = 0;
}
print(lineCount);
トップレベル変数とクラス変数は遅延初期化される。初期化コードは、その変数が最初に使われたときに実行される。
late変数
late
修飾子には2つの使用例がある:
- 宣言後に初期化されるNULLでない変数の宣言。
- 変数の初期化を怠る。
多くの場合、Dartの制御フロー解析は、非NULL変数が使用される前に非NULL値に設定されたことを検出できますが、解析に失敗することもあります。よくあるケースは、トップレベル変数とインスタンス変数です:Dartはこれらの変数が設定されているかどうかを判断できないことが多いため、試行しません。
変数が使用される前に設定されていることは確かだが、Dartがそれを認めない場合は、その変数をlate
としてマークすることでエラーを修正できる:
late String description;
void main() {
description = 'Feijoada!';
print(description);
}
Notice
late
変数の初期化に失敗すると、その変数が使用されるときに実行時エラーが発生する。
変数をlate
とマークしておきながら宣言時に初期化すると、その変数が最初に使われるときにイニシャライザーが実行される。この遅延初期化は、いくつかの場合に便利である:
- 変数が不要で、初期化にコストがかかる場合。
- インスタンス変数を初期化していて、そのイニシャライザーがこの変数にアクセスする必要がある。
次の例では、もしtemperature
変数が使われなければ、高価なreadThermometer()
関数が呼ばれることはありません:
// このプログラムでは、readThermometer()の呼び出しはこれだけである。
late String temperature = readThermometer(); // Lazily initialized.
final と const
変数を変更するつもりがない場合は、varの代わりに、あるいは型に加えて、finalまたはconstを使う。final変数は一度しか設定できないが、const変数はコンパイル時の定数である。∂const変数はコンパイル時の定数です(const変数は暗黙的にfinalとなります)。
Note
インスタンス変数はfinalにできるが、constにはできない。
以下は、final変数を作成して設定する例である:
final name = 'Bob'; // 型注釈なし
final String nickname = 'Bobby';
final変数の値を変更することはできない:
✗ static analysis: failuredart
name = 'Alice'; // エラー:final変数は一度しか設定できない。
コンパイル時に定数にしたい変数には const を使う。定数変数がクラス・レベルにある場合は、static const とします。変数を宣言する場所では、値を数値や文字列リテラル、const 変数、定数に対する算術演算の結果などのコンパイル時定数に設定します:
const bar = 1000000; // 圧力の単位(dynes/cm2)
const double atm = 1.01325 * bar; // 標準大気
constキーワードは、定数変数を宣言するためだけのものではありません。定数値を作成するコンストラクタの宣言にも使用できます。どんな変数でも定数値を持つことができます。
var foo = const [];
final bar = const [];
const baz = []; // `const []` に相当する。
上記のbazのように、const宣言の初期化式からconstを省略することができる。詳しくは、constを重複して使わないを参照してください。
finalでない、const でない変数の値を変更することができます:
foo = [1, 2, 3]; // Was const []
const変数の値を変更することはできない:
✗ static analysis: failuredart
baz = [42]; // エラー:定数変数に値を代入できません。
型チェックやキャスト(isやas)、コレクションif、スプレッド演算子(...や...?)を使用する定数を定義できます:
const Object i = 3; // ここで、i は int 値を持つ const オブジェクトである。
const list = [i as int]; // タイプキャストを使う。
const map = {if (i is int) i: 'int'}; // 使用はis、回収はif。
const set = {if (list is List<int>) ...list}; // ...とスプレッド。
Note
finalオブジェクトは変更できませんが、そのフィールドは変更できます。これに対して、constオブジェクトとそのフィールドは変更できません。
constを使って定数値を作成する方法の詳細については、リスト、マップ、およびクラスを参照してください。