Flutter(Dart)コンストラクタについて


## 1. コンストラクタとは
Classのインスタンスを作成したとき（newしたとき）に呼び出される初期化処理です。

## 2. Generative Constructors（生成的コンストラクタ）
通常、classをnewしたときに呼び出されるコンストラクタ
```dart
class Point {
  // インスタンス変数
  double x = 0;
  double y = 0;

  // コンストラクタ（class名と一緒。javaも同じっぽい）
  Point(double x, double y) {
    // ここでインスタンス変数を初期化する
    this.x = x;
    this.y = y;
  }
}
```

引数を`this.フィールド名`とするといちいち代入しなくても勝手に入れてくれます(Automatic field initialization)
```dart
class Point {
  double x;
  double y;

  // 第一引数
  Point(this.x, this.y);
}
```

ちなみに、`final`を設定している変数（finalはtsのconst相当）は最初に示した初期化方法が使えません。
https://doitu.info/blog/5c10f5358dbc7a001af33ce5
```dart
class Point {
  final double x;
  final double y;

  Point(double x, double y) {
    // 'x' can't be used as a setter because it's final.
    // と怒られる
    this.x = x;
    this.y = y;
  }
}
```

Automatic field initializationを使用するか、Initializer List（後述）での初期化であればできるとのこと

```dart
class Point {
  final double x;
  final double y;

  // Sets the x and y instance variables
  // before the constructor body runs.
  Point(this.x, this.y);
}
```

```dart
class Point {
  final double x;
  final double y;

  Point(double x1, double y1): x = x1, y = y1;
}
```

## 3. Named Constructors
2.で示したコンストラクタとは別にコンストラクタを作ることができます。

```dart
const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}


void main() {
  // xOrigin, yOriginが入ったPoint　classのインスタンスが作れる
  // Dartの場合、newは省略が可能
  var originPoint = Point.origin();
}
```


## 4. Redirecting Constructors
コンストラクタの処理を他のコンストラクタにリダイレクトすることができます。
```dart
class Point {
  double x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}
```
## 5. Initializer List

Initializer Listはコンストラクタの中身が実行される前に変数に値を入れることができます
コンストラクタの宣言の後ろに`: フィールド名x = 渡ってきた値など, フィールド名y = ...`と続きます。
```dart
class Point {
  double x;
  double y;

  Point(double x, double y): this.x = x, this.y = y1 {
    // ここに別のコンストラクター処理を入れる。ここが実行される前にxとyには既に値が存在する
  };
}
```

よくFlutterで出てくる、以下のようなコンストラクタもInitializer Listに含まれるとのこと。（Redirectではないらしい・・。）
MainContainerPageの初期化前に親コンストラクタを実行しています。
```dart
class MainContainerPage extends StatefulWidget {
  MainContainerPage({Key? key}) : super(key: key);
  ...
}
```

もしサブクラスでも何か初期化をしたい場合は`{}`を続けて記載するそうです
```dart
class MainContainerPage extends StatefulWidget {
  MainContainerPage({Key? key}) : super(key: key) {
    // サブクラスの初期化処理を書く
  }
  ...
}
```

## 6. Factories（ファクトリ）

javaのファクトリーパターンやシングルトンパターンを実装する時などに使える
`factory`を先頭につけたコンストラクタを設定すると、自動でインスタンスが作られず、コンストラクタ内で自分でインスタンスを生成することができる。
```dart

class Logger {
  final String name;
  bool mute = false;
  // _cacheはその名前がアンダスコアで始まっているのでライブラリ-プライベートである
  static final Map<String, Logger> _cache =
      <String, Logger>{};
  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }
  Logger._internal(this.name);
  void log(String msg) {
    if (!mute) print(msg);
  }
}
```

シングルトンパターンの実装例
```dart

class Singleton {
  static final Singleton _singleton = new Singleton._internal();
  factory Singleton() {
    return _singleton;
  }
  Singleton._internal();
}
//You can construct it with new
main() {
  var s1 = new Singleton();
  var s2 = new Singleton();
  print(identical(s1, s2));  // true
  print(s1 == s2);           // true
}
```
## 7. Constant Constructors（定数コンストラクタ)
コンパイル時に定数オブジェクトをインスタンス化するためのコンストラクタ。
同じ定数を引数とする場合、何度生成しても同一のインスタンスとなる
```dart

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
  final num x, y;
  const ImmutablePoint(this.x, this.y);
}

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!
```

ただし、別の定数を引数とした場合は（もちろん）同一インスタンスにならない。
また、呼び出し元でコンパイル時に決定しない（実行時に内容が決まる）引数を定数コンストラクタに渡すことはできず、よびだし元でconstをつけて呼び出すことができない。（同一インスタンスを生成できない）
呼び出し元でconstをつけずに呼び出すと、newと同じ効果となり、異なるインスタンスが生成される。const省略※とは異なるので注意する。

```dart
var a = const ImmutablePoint(1, 1); // 常数を生成
var b = ImmutablePoint(1, 1);       // 常数を生成しない(newと同じ）
assert(!identical(a, b));           // 同じインスタンスではない！
```

※const省略とは、constの内部でconstの定義をする場合、内部のconstを省くことができる
```dart

// ここでは多くのconstキーワードが使われている
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
// 最初に使われているconstキーワードを除いたすべてのconstは省略できる：

// 最初のconstのみでその他の常数コンテキストを確立することができる
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
```

同一定数でconstをつけて呼び出した場合は、factoryコンストラクタでのシングルトンパターンと同じような構成になるが、常にシングルトンになるわけではない。

## 8. 参考記事一覧
https://zenn.dev/semapho/articles/d0ee04f2446981
https://dev.classmethod.jp/articles/about_dart_constructors
https://dart.dev/guides/language/language-tour#constructors
https://www.cresc.co.jp/tech/java/Google_Dart2/language/classes/classes.html#__RefHeading__67589_754818896
https://doitu.info/blog/5c10f5358dbc7a001af33ce5
https://www.specialagentsqueaky.com/ja/blog-post/3ib3atqe/2014-01-12-how-to-call-the-super-class-constructor-in-dart-language/
https://minpro.net/two-types-of-const-in-dart