😽

【Dart入門(4)】型|組み込み型(Record)

に公開

Dartの組み込み型について

dartにはデフォルトで用意されている型が存在します。
以下はそのほかの言語でも用意されている型です。

よく使用する型
  • Numbers (int, double)
    • 数値に関する型で整数型と小数型
  • Strings (String)
    • 文字列に関する型
  • Booleans (bool)
    • 論理値に関する型。trueかfalseか
  • Records ((value1, value2))
    • 固定長で異なる型が共存できる型。タプルに該当
  • Functions (Function)
    • 関数型。関数もオブジェクトなので型があります。
  • Lists (List, also known as arrays)
    • 配列型
  • Sets (Set)
    • 集合型
  • Maps (Map)
    • 辞書型
  • Runes (Runes; often replaced by the characters API)
  • Symbols (Symbol)
  • The value null (Null)
    • 何もないを意味する型

これ以外に、dart特有の型も存在します。

dart特有の型
  • Object: Null型を除いた全ての型の親となる型。
    • Null以外はObject型を継承している。
  • Enum: 列挙型。
  • Future and Stream: 非同期処理で使用される型。
    • Future: 非同期型でawaitしてその結果を取得できる。
    • Stream: web socket 通信のように常にどこかと通信して結果を受け取る型。
  • Iterable: for ループで使用される型。
  • Never: 必ず成功しないことを意味する型。必ずエラーを吐く関数などが返す型。
  • dynamic: 型が決定しない型。大抵はObject型やObject?を使用した方が良い。
  • void: 値が使用されないことを意味する型。何も返却しない関数が返す。

これらの型は今すぐ理解する必要はありませんが、頭の片隅にこのような型があると認識しておくことが大切です。


組み込み型を詳しくみていく(Record)

先ほど紹介した型のよく使用する型の使い方を詳しくみていきましょう。

Record

よく出てくる型で少し独特なので丁寧にみていきましょう。


Recordって?

Record とは?

Dart の Record は 「複数の値をひとまとめにした1つの値」 のことです。
ポイントは以下です:

  1. 匿名 (anonymous)
    クラスのように名前を付けなくても、その場でまとめて使える。

  2. イミュータブル (immutable)
    一度作ったら中身を変更できない。だから安心してデータを扱える。

  3. 固定サイズ (fixed-sized)
    まとめられる要素の数は決まっている。後から増やしたり減らしたりはできない。

  4. 異なる型を持てる (heterogeneous)
    int と String と bool を同時に1つの Record に入れることができる。
    → List は「全部同じ型」になることが多いけど、Record はバラバラOK。

  5. 型付けされている (typed)
    (int, String) というように、「中に入る型」まで Dart が把握してくれる。

タプルに似ていますね。

Recordとは、各要素が括弧の中で名前付きか位置によって特徴づけられ、それらがコンマによって分割されるリストのことです。

(String, {int a, bool b}, String) record = ('first', a: 2, b: true, 'last');

名前付きかそうでないかは非常に重要です。
以下の例で見られるように、

  • 位置のみで特徴付けられるrecordは同じ位置の要素が同じオブジェクトなら同一のrecordとして扱われる
  • 逆に名前付きの場合、各要素が同じ位置で同じオブジェクトでも別々のrecordとして扱われます。
(int, int) a1 = (2,3);
(int, int) a2 = (2,3);

assert(a1 == a2);

// これは名前付きではない。ただのアノテーション
(int c, int d) l1 = (2,3);
(int o, int p) l2 = (2,3);

assert(l1 == l2);

({int x, int y}) b1 = (x: 3, y: 5);
({int r, int s}) b2 = (r: 3, s: 5);

assert(b1 != b2);

Recordの要素へのアクセス方法

下の例にあるように位置は位置、名前付きは名前付きで区別してアクセスします。

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'

関数の戻り値とRecord

関数が複数の値を一度に返却したいときにはRecordが役立ちます。

  1. 戻り値が位置の場合
// Returns multiple values in a record:
// nameとageはアノテーションなので、可読性を上げるには役立つ
// (String, int)を返却でも良いが、なんの値なのかはわからない
(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);
  1. 戻り値が名前付きの場合
// Returns multiple values in a record:
// nameとageはアノテーションなので、可読性を上げるには役立つ
// (String, int)を返却でも良いが、なんの値なのかはわからない
({String name, int age}) userInfo() {
  return (name: "kitao", age: 87);
}

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

var (name: name, age: age) = userInfo(json);
// 省略記法
var (:name, :age) = userInfo(json);
print(name);
print(age);
// kitao
// 87
  1. 戻り値が位置と名前付きの場合
void main() {
  (int, {String name, int age}) userInfo() {
    return (1, name: "kitao", age: 87);
  }

  // Destructures using a record pattern with positional fields:
  var (number, name: myName, age: myAge) = userInfo();
  print(number);
  print(myName);
  print(myAge);
}
//1
//kitao
//87

まとめ

  • Recordは複数の値をひとまとめにした1つの値
  • Recordの要素には異なる型のインスタンスを入れることができる
  • Recordは位置と名前によって要素が関連づけられている

Recordにはその他にも応用的な使用方法があるのですが、今回は割愛して、後半の方で解説できたらと思います。

Discussion