Dartでenhanced enumsを有効にしてEnumのFactoryを使ってみた

2022.11.08

こんにちは、CX事業本部 IoT事業部の若槻です。

最近Flutterアプリ開発でDart言語をよく触っているのですが、今までほぼJS/TS(およびPythonを少々)しか触ってこなかったため、Dartの言語仕様のキャッチアップにヒーヒー言いながら努めています。

今回は、そんなDartを使う中でenhanced enumsを有効にしたEnumsのFactoryを使うことがあったので共有します。

enhanced enumsとは

Dartで使用できるEnumsには、simple enumsenhanced enumsの2種類があります。

simple enumsは通常のEnumsです。宣言はenumキーワードを使用して値の列挙を行うだけです。

simple enums

enum Color { red, green, blue }

一方でenhanced enumsは、次の例のように通常のEnumsに対してクラスのような拡張を行うことが可能です。

enhanced enums

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();

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

使ってみる

enhanced enumを有効にする

というわけで早速enhanced enumを使おうとしたのですが、Enumsの列挙の末尾に;を追加するとエラーが発生しました。

enum City {
  tokyo,
  osaka,
  nagoya,
  other;

}

どうやらenhanced enumを有効にするためには、pubspec.yamlでSDKのバージョンを2.17.0以上に上げる必要があるようです。

This requires the 'enhanced-enums' language feature to be enabled.
Try updating your pubspec.yaml to set the minimum SDK constraint to 2.17.0 or higher, and running 'pub get'.dart(experiment_not_enabled)

pubspec.yamlを次のように編集します。

pubspec.yaml

environment:
-   sdk: ">=2.16.2 <3.0.0"
+   sdk: ">=2.17.0 <3.0.0"

するとエラーが消えました。有効にできたようです。

Factoryを使ってみる

それではenhanced enumsを使ってEnumsに対してFactoryを実装してみます。

まず、enhanced enumsを下記のようにしてFactoryを作ろうとたのですが、エラーが発生しました。

enum City {
  tokyo,
  osaka,
  nagoya;

  factory City.fromName(String name) {
    switch (name) {
      case '東京':
        return City.tokyo;
      case '大阪':
        return City.osaka;
      case '名古屋':
        return City.nagoya;
    }
  }
}

エラーメッセージによると、null含めてEnums一覧にない値がreturnされうる記述はだめとのこと。

The body might complete normally, causing 'null' to be returned, but the return type, 'City', is a potentially non-nullable type.
Try adding either a return or a throw statement at the end.dartbody_might_complete_normally

ドキュメントにもそのように記述がありますね。

Factory constructors can only return one of the fixed, known enum instances.

Factoryのcase文を修正し、最終的に下記のようなコードを作りました。

lib/sample.dart

enum City {
  tokyo,
  osaka,
  nagoya,
  other;

  factory City.fromName(String name) {
    switch (name) {
      case '東京':
        return City.tokyo;
      case '大阪':
        return City.osaka;
      case '名古屋':
        return City.nagoya;
    }
    return City.other;
  }
}

main() {
  print(City.fromName('東京') == City.tokyo);
  print(City.fromName('大阪') == City.tokyo);
  print(City.fromName('名古屋') == City.tokyo);
  print(City.fromName('出雲') == City.tokyo);
}

City.fromNameで引数に応じたCityの列挙値が取得でき、判定が行えていることが確認できます。

$ dart lib/sample.dart
true
false
false
false

おわりに

Dartでenhanced enumsを有効にしてEnumのFactoryを使ってみました。

enhanced enumを使ってひとまずやりたいことが実現できたので良かったです。またドキュメントを見るとFactory以外にも色々できそうなことがあるようなので今後も活用していきたいと思います。

以上