Skip to content

レコード

Version note

記録には少なくとも3.0の言語バージョンが必要です。

レコードは匿名、不変、集約型である。他のコレクション型と同様、複数のオブジェクトを1つのオブジェクトにまとめることができる。他のコレクション型と異なり、レコードは固定サイズ、異種、型付きである。

レコードは実際の値であり、変数に格納したり、ネストしたり、関数に渡したり、関数から渡したり、リスト、マップ、セットなどのデータ構造に格納したりすることができる。

レコード構文

レコード式は、名前フィールドまたは位置フィールドのカンマ区切りのリストで、括弧で囲まれている:

dart
var record = ('first', a: 2, b: true, 'last');

レコード型アノテーションは、括弧で囲まれたカンマ区切りの型リストです。レコード型アノテーションを使用して、戻り値の型やパラメータの型を定義することができます。たとえば、次の (int, int) ステートメントはレコード型アノテーションです:

dart
(int, int) swap((int, int) record) {
  var (a, b) = record;
  return (b, a);
}

レコード式と型アノテーションのフィールドは、関数におけるパラメータと引数の働きを反映している。位置フィールドは括弧の中に直接入る:

dart
// 変数宣言の型アノテーションを記録する:
(String, int) record;

// レコード式で初期化する:
record = ('A string', 123);

レコード型注釈では、名前付きフィールドは、すべての位置フィールドの後に、型と名前のペアで中かっこで区切られたセクションの中に入る。レコード式では、名前は各フィールド値の前に置かれ、その後にコロンが置かれる:

dart
// 変数宣言の型アノテーションを記録する:
({int a, bool b}) record;

// レコード式で初期化する:
record = (a: 123, b: true);

レコード型の名前付きフィールドの名前は、レコードの型定義の一部、つまりその形である。異なる名前の名前付きフィールドを持つ2つのレコードは、異なる型を持つ:

dart
({int a, int b}) recordAB = (a: 1, b: 2);
({int x, int y}) recordXY = (x: 3, y: 4);

// コンパイルエラーです!これらのレコードは同じ型を持っていません。
// recordAB = recordXY;

レコード型アノテーションでは、位置フィールドに名前を付けることもできるが、これらの名前は純粋に文書化のためのものであり、レコードの型には影響しない:

dart
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);

recordAB = recordXY; // OK.

これは、関数宣言や関数typedefの位置パラメーターが名前を持つことができるが、それらの名前が関数のシグネチャーに影響を与えないのと似ている。

詳細と例については、レコードの種類とレコードの等式をご覧ください。

レコードフィールド

レコード・フィールドは組み込みのゲッターでアクセスできる。レコードは不変なので、フィールドはセッターを持ちません。

名前付きフィールドは同じ名前のゲッターを公開します。位置フィールドは、$<position>という名前のゲッターを公開します:

dart
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'

レコード・フィールドへのアクセスをさらに効率化するには、パターンのページをご覧ください。

レコードの種類

個々のレコード型に対する型宣言はない。レコードはそのフィールドの型に基づいて構造的に型付けされる。レコードの形(そのフィールドの集合、フィールドの型、もしあればその名前)は、レコードの型を一意に決定する。

レコードの各フィールドはそれぞれ独自の型を持っている。フィールドの型は同じレコード内でも異なることがある。型システムは、レコードからアクセスされるどの場所でも、各フィールドの型を認識する:

dart
(num, Object) pair = (42, 'a');

var first = pair.$1; // 静的な型は `num` で、実行時の型は `int` である。
var second = pair.$2; // 静的型は `Object`、実行時型は `String` である。

同じフィールドのセットを持つレコードを作成する、関係のない2つのライブラリを考えてみよう。型システムは、ライブラリが互いに結合されていなくても、それらのレコードが同じ型であることを理解する。

レコードの平等性

2つのレコードが同じ形状(フィールドのセット)を持ち、対応するフィールドが同じ値を持つ場合、2つのレコードは等しい。名前付きフィールドの順序はレコードの形状の一部ではないので、名前付きフィールドの順序は等しさに影響しない。

例えば:

dart
(int x, int y, int z) point = (1, 2, 3);
(int r, int g, int b) color = (1, 2, 3);

print(point == color); // 'true' と表示される。
dart
({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と==メソッドを定義する。

マルチプル・リターン

レコードを使用すると、関数は複数の値をまとめて返すことができます。戻り値からレコード値を取り出すには、パターン・マッチを使って値をローカル変数に再構築します。

dart
// レコード内の複数の値を返します:
(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のドキュメントを参照してください。