Dart のコンストラクタを理解する

エキサイト株式会社の武藤です。

Flutterを使ったアプリ開発のプロジェクトにジョインしました。 Dartを触ってみて、コンストラクタにいくつか種類があることがわかったので、整理してみたいと思います。

コンストラク

通常のコンストラクタです。コンストラクタ引数をプロパティーにセットします。

class Book {
  String title = '';
  
  Book(String title) {
    this.title = title;
  }
}

void main() {
  var book = Book('my book');

  print(book.title); // my book
}

上記を省略して、下記のように記述できます。

class Book {
  String title;

  Book(this.title);
}

この記法の場合では、プロパティーの初期値を設定しないで済みます。

デフォルトコンストラク

コンストラクタを記述しない場合、デフォルトとして引数を持たないコンストラクタがコンパイル時に定義されます。

class Book {
  String title = "my book";
}

void main() {
  var book = Book();
  
  print(book.title); // my book
}

Dartでは、サブクラスはスーパークラスのコンストラクタを継承しません。そのため、サブクラスでコンストラクタを定義しない場合にデフォルトのコンストラクタが定義されます。

名前付きコンストラク

コンストラクタに名前をつけて、用途に応じて呼び出すことができます。

class Blog {
  String title;
  
  Blog(this.title);
  Blog.draft() : title = 'draft title';
}

void main() {
  var book = Blog.draft();
  
  print(book.title); // draft title
}

リダイレクトコンストラク

コンストラクタから別のコンストラクタを呼び出すことができます。 名前付きコンストラクタと似ており、用途に応じて別名を付けられます。

class Todo {
  Todo(this.name, this.elapsedTime);
  
  Todo.newTask(): this('new task', Duration());
  
  String name;
  // 経過時間
  Duration elapsedTime;
}


void main() {
  var todo =Todo.newTask();

  print(todo.name); // new task
  print(todo.elapsedTime); // 0:00:00.000000
}

定数コンストラク

オブジェクトを定数として扱う際に使われます。コンストラクタに const をつけます。

class HttpStatus {
  const HttpStatus();
  final int OK = 200;
  final int BAD_REQUEST = 400;
}

void main() {
  var httpStatus = HttpStatus();
  print(httpStatus.OK);
}

オブジェクト定数を扱う場合はパフォーマンスを考慮して、複数回のインスタンス生成を避けたいことがあります。

下記のように、private constructorにより一度だけインスタンス生成をします。 インスンタンス生成が外部からされなくなり、インスンタンスを1つに保つことができます。

class HttpStatus {
  const HttpStatus._();

  static final int OK = 200;
  static final int BAD_REQUEST = 400;
}

void main() {
  // コンストラクタにアクセスできないのでエラーになる
  // var httpStatus = new HttpStatus(); 

  print(HttpStatus.OK);
}

ファクトリコンストラク

ファクトリコンストラクタでは、コンストラクタのロジックを独自に記載できます。 それは、例えば、常に新しいインスタンス生成せず、既に生成されたインスタンスを返すシングルトンパターンの実装に使えます。

class Book {
  String title;
  static var _instance;
  
  factory Book(String title){
    _instance ??= Book._internal(title);
    return _instance;
  }

  Book._internal(this.title);
}

void main() {
  var book1 = Book('my book1');
  var book2 = Book('my book2');
  
  print(book1.title);  // my book1 
  print(book2.title); // my book1
  print(book1 == book2); // true
}

book2はbook1と同一のインスタンスとなります。

ファクトリコンストラクタは、単純な生成処理以外にロジックをもたせることもできます。 その一例として、Flutterで使われるFreezedパッケージのJSONのパース処理等を見かけます。 下記のコードは、公式ページから引用したものです。

@freezed
class Person with _$Person {
  const factory Person({
    required String firstName,
    required String lastName,
    required int age,
  }) = _Person;

  factory Person.fromJson(Map<String, Object?> json)
      => _$PersonFromJson(json);
}

終わりに

Dartを書くにあたり、複数のコンストラクタが出てきたので、その種類について整理しました。 これまでにPHPJava、Kotlin等を触ってきましたが、初めて触る機能があり、とてもおもしろかったです。

参考になれば幸いです。

参考文献

dart.dev

pub.dev