目次

レコード

レコードは、匿名の、不変な、集約型です。他のコレクション型と同様に、複数のオブジェクトを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
// Record type annotation in a variable declaration:
(String, int) record;

// Initialize it with a record expression:
record = ('A string', 123);

レコード型注釈では、名前付きフィールドは、すべての位置指定フィールドの後に、型と名前のペアの中波括弧で囲まれたセクション内に記述します。レコード式では、名前は各フィールド値の前にコロンを付けて記述します。

dart
// Record type annotation in a variable declaration:
({int a, bool b}) record;

// Initialize it with a record expression:
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);

// Compile error! These records don't have the same type.
// 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; // Static type `num`, runtime type `int`.
var second = pair.$2; // Static type `Object`, runtime type `String`.

同じフィールドセットを持つレコードを作成する、関連のない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); // Prints '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); // Prints 'false'. Lint: Equals on unrelated types.

レコードは、フィールドの構造に基づいて、自動的に`hashCode`と`==`メソッドを定義します。

複数戻り値

#

レコードを使用すると、関数は複数の値をまとめて返すことができます。戻り値からレコード値を取得するには、分割代入を使用して、パターンマッチングを使用してローカル変数に値を分割代入します。

dart
// Returns multiple values in a record:
(String name, int age) userInfo(Map<String, dynamic> json) {
  return (json['name'] as String, json['age'] as int);
}

final json = <String, dynamic>{
  'name': 'Dash',
  'age': 10,
  'color': 'blue',
};

// Destructures using a record pattern with positional fields:
var (name, age) = userInfo(json);

/* Equivalent to:
  var info = userInfo(json);
  var name = info.$1;
  var age  = info.$2;
*/

パターンの型ページで詳しく説明されているコロン`:`構文を使用して、名前付きフィールドを使用してレコードを分割代入することもできます。

dart
({String name, int age}) userInfo(Map<String, dynamic> json)
// ···
// Destructures using a record pattern with named fields:
final (:name, :age) = userInfo(json);

レコードを使用せずに、関数から複数の値を返すことはできますが、他の方法には欠点があります。たとえば、クラスの作成ははるかに冗長になり、`List`や`Map`などの他のコレクション型を使用すると、型の安全性が失われます。