レコード
Version note
記録には少なくとも3.0の言語バージョンが必要です。
レコードは匿名、不変、集約型である。他のコレクション型と同様、複数のオブジェクトを1つのオブジェクトにまとめることができる。他のコレクション型と異なり、レコードは固定サイズ、異種、型付きである。
レコードは実際の値であり、変数に格納したり、ネストしたり、関数に渡したり、関数から渡したり、リスト、マップ、セットなどのデータ構造に格納したりすることができる。
レコード構文
レコード式は、名前フィールドまたは位置フィールドのカンマ区切りのリストで、括弧で囲まれている:
var record = ('first', a: 2, b: true, 'last');
レコード型アノテーションは、括弧で囲まれたカンマ区切りの型リストです。レコード型アノテーションを使用して、戻り値の型やパラメータの型を定義することができます。たとえば、次の (int, int) ステートメントはレコード型アノテーションです:
(int, int) swap((int, int) record) {
var (a, b) = record;
return (b, a);
}
レコード式と型アノテーションのフィールドは、関数におけるパラメータと引数の働きを反映している。位置フィールドは括弧の中に直接入る:
// 変数宣言の型アノテーションを記録する:
(String, int) record;
// レコード式で初期化する:
record = ('A string', 123);
レコード型注釈では、名前付きフィールドは、すべての位置フィールドの後に、型と名前のペアで中かっこで区切られたセクションの中に入る。レコード式では、名前は各フィールド値の前に置かれ、その後にコロンが置かれる:
// 変数宣言の型アノテーションを記録する:
({int a, bool b}) record;
// レコード式で初期化する:
record = (a: 123, b: true);
レコード型の名前付きフィールドの名前は、レコードの型定義の一部、つまりその形である。異なる名前の名前付きフィールドを持つ2つのレコードは、異なる型を持つ:
({int a, int b}) recordAB = (a: 1, b: 2);
({int x, int y}) recordXY = (x: 3, y: 4);
// コンパイルエラーです!これらのレコードは同じ型を持っていません。
// recordAB = recordXY;
レコード型アノテーションでは、位置フィールドに名前を付けることもできるが、これらの名前は純粋に文書化のためのものであり、レコードの型には影響しない:
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);
recordAB = recordXY; // OK.
これは、関数宣言や関数typedefの位置パラメーターが名前を持つことができるが、それらの名前が関数のシグネチャーに影響を与えないのと似ている。
詳細と例については、レコードの種類とレコードの等式をご覧ください。
レコードフィールド
レコード・フィールドは組み込みのゲッターでアクセスできる。レコードは不変なので、フィールドはセッターを持ちません。
名前付きフィールドは同じ名前のゲッターを公開します。位置フィールドは、$<position>
という名前のゲッターを公開します:
var record = ('first', a: 2, b: true, 'last');
print(record.$1); // Prints 'first'
print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'
レコード・フィールドへのアクセスをさらに効率化するには、パターンのページをご覧ください。
レコードの種類
個々のレコード型に対する型宣言はない。レコードはそのフィールドの型に基づいて構造的に型付けされる。レコードの形(そのフィールドの集合、フィールドの型、もしあればその名前)は、レコードの型を一意に決定する。
レコードの各フィールドはそれぞれ独自の型を持っている。フィールドの型は同じレコード内でも異なることがある。型システムは、レコードからアクセスされるどの場所でも、各フィールドの型を認識する:
(num, Object) pair = (42, 'a');
var first = pair.$1; // 静的な型は `num` で、実行時の型は `int` である。
var second = pair.$2; // 静的型は `Object`、実行時型は `String` である。
同じフィールドのセットを持つレコードを作成する、関係のない2つのライブラリを考えてみよう。型システムは、ライブラリが互いに結合されていなくても、それらのレコードが同じ型であることを理解する。
レコードの平等性
2つのレコードが同じ形状(フィールドのセット)を持ち、対応するフィールドが同じ値を持つ場合、2つのレコードは等しい。名前付きフィールドの順序はレコードの形状の一部ではないので、名前付きフィールドの順序は等しさに影響しない。
例えば:
(int x, int y, int z) point = (1, 2, 3);
(int r, int g, int b) color = (1, 2, 3);
print(point == color); // 'true' と表示される。
({int x, int y, int z}) point = (x: 1, y: 2, z: 3);
({int r, int g, int b}) color = (r: 1, g: 2, b: 3);
print(point == color); // 'false' を出力する。Lint: 関連性のない型の等号。
レコードはフィールドの構造に基づいて自動的にhashCodeと==メソッドを定義する。
マルチプル・リターン
レコードを使用すると、関数は複数の値をまとめて返すことができます。戻り値からレコード値を取り出すには、パターン・マッチを使って値をローカル変数に再構築します。
// レコード内の複数の値を返します:
(String, int) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}
final json = <String, dynamic>{
'name': 'Dash',
'age': 10,
'color': 'blue',
};
// レコードパターンを使って破壊する:
var (name, age) = userInfo(json);
/* 以下と同等:
var info = userInfo(json);
var name = info.$1;
var age = info.$2;
*/
関数からレコードなしで複数の値を返すことができるが、他のメソッドには欠点がある。例えば、クラスの作成はより冗長になりますし、ListやMapのような他のコレクション・タイプを使用すると、型の安全性が失われます。
Note
レコードの複数返却と異種タイプの特性により、異なるタイプの先物の並列化が可能。dart:asyncのドキュメントを参照してください。