【Flutter/Dart】enum, enhanced enumsについてとenumの要素→文字列に変換する方法

はじめに

こんにちは。エキサイト株式会社でエンジニアをしている新卒の岡島です。 普段業務ではFlutterを用いたアプリ開発を行っています。 今回は、業務中にenumについて学んだことがあるので、勉強したことも含めて共有していきたいと思います。

私は、列挙型は単に列挙するだけだと思っていたのですが、想像以上に奥が深くて勉強になりました。前半では公式ドキュメントでの言語仕様を確認し、後半ではenumの使用例について書いていこうと思います。

列挙型(enum)とは?

enumは、名前付きの値(定数)を列挙するためのデータ型です。enumを用いることで値に意味を持たせることができ、可読性と保守性の向上に繋がります。

環境

Dart バージョン: 3.4.3

Enhanced enumsを使用するために、Dartのバージョン2.17以上が必要ですので注意してください。

Dartでのenumの定義

シンプルなenum

Dartenumを定義する方法を以下に例を示します。

enum Weekday {
  monday,
  tuesday,
  wednesday,
  thursday,
  friday,
  saturday,
  sunday
}

enhanced enums

Dartでは、フィールド、メソッド、constコンストラクタを持つクラスとしてenumを宣言することもできます。

enhanced enumは通常の class と同様に扱えますが、以下のような追加要件があります。

enhanced enumの要件

  • インスタンス変数は、finalである必要がある。
  • すべての生成コンストラクターは定数である必要がある。
  • 他のクラスを拡張することはできない
  • Factoryコンストラクタはenum インスタンスの 1 つだけを返す。
  • index 、 hashCode 、等価演算子 == をオーバーライドすることはできない。
  • values という名前のメンバーは列挙型で宣言できない。(values ゲッターと競合するため)

公式ドキュメントの例

enum Vehicle implements Comparable<Vehicle> {
  car(tires: 4, passengers: 5, carbonPerKilometer: 400),
  bus(tires: 6, passengers: 50, carbonPerKilometer: 800),
  bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0);

  const Vehicle({
    required this.tires,
    required this.passengers,
    required this.carbonPerKilometer,
  });

  final int tires;
  final int passengers;
  final int carbonPerKilometer;

  int get carbonFootprint => (carbonPerKilometer / passengers).round();

  bool get isTwoWheeled => this == Vehicle.bicycle;

  @override
  int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;
}

enumの使用方法

使用方法について公式ドキュメントに則り、以下の例を用いて説明していきます。

enum Color { red, green, blue }

インスタンスへのアクセス

列挙された値にはstatic変数と同様にアクセスできます。

final favoriteColor = Color.blue;
if (favoriteColor == Color.blue) {
  print('Your favorite color is blue!');
}

インデックスの取得

enum値にはindexゲッターがあり、enum 宣言内の値の 0 から始まる位置を返します。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

全ての値のリスト化

enumのvalues定数を使用すると、列挙された全ての値のリストを取得できます。

  final List<Color> colors = Color.values;
  print(colors); // [Color.red, Color.green, Color.blue]
  print(colors[2]==Color.blue); // true

switch文での使用

enumをswitch文で使用すると、全てのenum値を処理しなければ警告が表示されます。

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
  case Color.green:
    print('Green as grass!');
  default: // これがないとWARNINGが表示されます。
    print(aColor); // 'Color.blue'
}

名前の取得

列挙された値の名前を取得するには、.nameプロパティを使用します。

print(Color.blue.name); // 'blue'

enumの要素から文字列、文字列からenumへ変換する

Dartenumでは、フィールドを用いて簡単に文字列への変換をすることができます。

また、文字列からenumへ変換する場合は、標準でbyNameメソッドが用意されています。このメソッドを使うと、文字列から対応するenumの要素を取得できます。 水星→Planet.mercuryのように文字列をenumに変換したい場合はenumの中にgetPlanetFromStringのようにメソッドを作ることもできます。あらかじめ文字列とenumのMapを用意しておくことで、列挙する要素が増えても、文字列からenumの要素への変換が高速にすることができます。

以下に例を挙げて説明していきます。

enum Planet {
  mercury("水星"),
  venus("金星"),
  mars("火星"),
  jupiter("木星"),
  saturn("土星"),
  othor("その他");

  const Planet(this.japaneseName);

  final String japaneseName;

  static final Map<String, Planet> _map = {
    for (final planet in Planet.values) planet.japaneseName: planet
  };

  static Planet getPlanetFromString(String value) {
    return _map[value] ?? Planet.othor;
  }
}
print(Planet.mars.japaneseName); // 火星

Planetの各要素には、japaneseNameフィールドに日本語名が追加されています。これにより、japaneseNameフィールドにアクセスすることで簡単にenumの要素を文字列に変換できます。

print(Planet.values.byName("mercury")); // Planet.mercury

標準で用意されているbyNameメソッドにより、文字列からenumの要素を守則できます。

print(Planet.getPlanetFromString("金星")); // Planet.venus

カスタムメソッドを用意すれば、特定の文字列からenumの要素を取得できます。

まとめ

Dartenumを使用すると、enumの要素と文字列の相互変換が簡単に行えます。byNameメソッドやカスタムメソッドを活用することで、柔軟な文字列変換が可能です。この記事を通じて、enumの基本から応用までを理解し、今後の開発に役立てていただければ幸いです。