コンストラクタ

コンストラクタとは何か

コンストラクタはクラスのインスタンスを生成するための特殊な関数で、主にプロパティの初期化に使用されます。デフォルトコンストラクタを除き、すべてのコンストラクタの名前はクラス名と同じになります。

Dartのコンストラクタ分類:

  1. 生成コンストラクタ(Generative)
  2. デフォルトコンストラクタ(Default)
  3. 名前付きコンストラクタ(Named)
  4. 定数コンストラクタ(Const)
  5. リダイレクトコンストラクタ(Redirecting)
  6. ファクトリコンストラクタ(Factory)
  7. リダイレクトファクトリコンストラクタ
  8. 簡略コンストラクタ構文(Dart 3.13以降)
  9. プライマリコンストラクタ(フィールド簡略記述+コンストラクタ)

1. 生成コンストラクタ Generative Constructor

最も基礎的なコンストラクタで、インスタンスを作成しメンバー変数を初期化します。初期化仮引数 this.変数に対応し、代入処理を簡略化できます。

class Point {
  double x;
  double y;

  // 生成コンストラクタ、this.xで引数を直接メンバーに代入
  Point(this.x, this.y); // x、yに直接値を代入
}

void main() {
  final p = Point(10, 20);
  print("(${p.x}, ${p.y})"); // (10, 20)
}
Code language: JavaScript (javascript)

2. デフォルトコンストラクタ Default Constructor

クラスに手動でコンストラクタを定義しない場合、Dartは引数なし・名前なしの生成コンストラクタを自動的に提供します。C++、Java、C#など他のC系言語も同様の仕様です。

class PointA {
  double x = 0;
  double y = 0;
  // 暗黙のデフォルトコンストラクタ:PointA();
}

void main() {
  final p = PointA(); // 自動的にデフォルトコンストラクタが呼び出される
  print("(${p.x}, ${p.y})"); // (0.0, 0.0)
}
Code language: PHP (php)

注意:手動で任意のコンストラクタを記述すると、デフォルトコンストラクタは消滅します。

3. 名前付きコンストラクタ Named Constructor

用途:1つのクラスに複数のコンストラクタを定義し、意味を明確に分ける。書式:クラス名.コンストラクタ名()

const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  // 基本の生成コンストラクタ
  Point(this.x, this.y);

  // 名前付きコンストラクタ:原点
  Point.origin() : x = xOrigin, y = yOrigin;
}

void main() {
  final p1 = Point(5, 6);
  final p2 = Point.origin(); // 名前付きコンストラクタを呼び出す
  print(p1.x); //5
  print("原点:(${p2.x}, ${p2.y})"); // (0,0)
}
Code language: PHP (php)

重要ルール

  • サブクラスは親クラスの名前付きコンストラクタを継承しない。同名の名前付きコンストラクタを使用したい場合は手動で実装する必要がある;
  • 名前付きコンストラクタは初期化リストによるfinal変数への代入に対応している。

4. 定数コンストラクタ Const Constructor

コンパイル時定数オブジェクトを作成するために使用、条件:

  1. すべてのメンバー変数に final 修飾子を付ける
  2. コンストラクタに const を付記し、インスタンス生成時も const を使用すると定数キャッシュが共有される。constを省略すると通常の別オブジェクトが作成される。
class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0); // クラス共通の静的定数、全体で同一インスタンスを共有
  final double x, y;

  // 定数コンストラクタはfinalなx、yを初期化可能
  const ImmutablePoint(this.x, this.y);
}

void main() {
  // コンパイル時定数、同一オブジェクトを再利用
  const p1 = ImmutablePoint(1, 2);
  const p2 = ImmutablePoint(1, 2); // 引数が完全一致すると同じインスタンス
  print(p1 == p2); // true

  // 通常インスタンス、新規オブジェクト
  final p3 = ImmutablePoint(1, 2);
  print(p1 == p3); // false
}Code language: PHP (php)

5. リダイレクトコンストラクタ Redirecting Constructor

: this(引数) を使用し、同一クラス内の別のコンストラクタに処理を転送するコンストラクタ。コンストラクタ本体は空にする必要がある

class Point {
  double x, y;

  // 基本コンストラクタ
  Point(this.x, this.y);

  // リダイレクトコンストラクタ:xだけ受け取りyを0固定、基本コンストラクタに転送
  Point.alongX(double x) : this(x, 0); // 名前付きコンストラクタから基本コンストラクタに引数を渡しy=0を固定
}

void main() {
  final p = Point.alongX(9);
  print("x=${p.x}, y=${p.y}"); // x=9, y=0
}
Code language: JavaScript (javascript)

6. ファクトリコンストラクタ Factory Constructor

factory キーワードを使用。活用場面:

  1. 必ずしも新規オブジェクトを作成しない(キャッシュ再利用、サブクラスインスタンスを返却)
  2. インスタンス作成前に複雑なロジックを実行(JSONパース、引数検証)。制限:this にアクセスできない
キャッシュによるシングルトン Logger 例
class Logger {
  final String name;
  bool mute = false;
  // ライブラリ内のみ参照可能なプライベートキャッシュ
  static final Map<String, Logger> _cache = {};

  // ファクトリコンストラクタ:優先的にキャッシュから取得
  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // ファクトリコンストラクタ:JSONからインスタンス生成
  factory Logger.fromJson(Map<String, dynamic> json) {
    return Logger(json["name"].toString());
  }

  // 内部専用プライベート生成コンストラクタ
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

void main() {
  final log1 = Logger("UI");
  final log2 = Logger("UI");
  print(log1 == log2); // true 同一のキャッシュオブジェクト

  final jsonLog = Logger.fromJson({"name": "Net"});
  jsonLog.log("request success");
}
Code language: PHP (php)

7. リダイレクトファクトリコンストラクタ Redirecting Factory

書式 factory 名前() = 他クラス.コンストラクタ;。抽象クラスでインスタンス生成を委譲する際に適し、引数の重複記述を省略できる。

abstract class Listenable {}

class _MergingListenable implements Listenable {
  List<_MergingListenable> list;
  _MergingListenable(this.list);
}

// 内部プライベートクラスのコンストラクタに転送するリダイレクトファクトリ
factory Listenable.merge(List<Listenable> list) = _MergingListenable;
Code language: PHP (php)

8. コンストラクタティアオフ Constructor Tear-offs

コンストラクタを括弧 () なしで直接関数引数として渡せる。冗長なラムダ無名関数の代わりに使用し、パフォーマンスが向上する。

推奨記述
void main() {
  final charCodes = [65, 66, 67];
  // 名前付きコンストラクタ String.fromCharCode
  final strs = charCodes.map(String.fromCharCode);
  print(strs); // (A, B, C)

  // .new による無名コンストラクタティアオフ
  final buffers = charCodes.map(StringBuffer.new);
}
Code language: PHP (php)
非推奨:冗長なラムダ
// 余分なラップ、推奨されない
charCodes.map((code) => String.fromCharCode(code));
Code language: JavaScript (javascript)

9. Dart 3.13以降 簡略コンストラクタ構文 Concise Constructor

クラス内部でクラス名を省略し、new / factory から直接コンストラクタを定義可能。クラス名.xxx の記述が不要:

従来記法簡略記法
Point(this.x,this.y)new(this.x,this.y)
Point.origin():x=0,y=0new origin():x=0,y=0
factory Point.clone()factory clone()
class Point {
  double x, y;

  // 簡略化された無名生成コンストラクタ
  new(this.x, this.y);

  // 簡略化された名前付き生成コンストラクタ
  new origin() : x = 0, y = 0;

  // 簡略化されたファクトリコンストラクタ
  factory clone(Point other) => new(other.x, other.y);
}

void main() {
  final p = new(3, 4);
  final origin = new origin();
  final copy = p.clone(p);
}
Code language: PHP (php)

10. インスタンス変数初期化3種類の方法

1:宣言時に直接代入
class PointA {
  double x = 1.0;
  double y = 2.0;
}
Code language: JavaScript (javascript)

2:初期化仮引数

this.変数

オプショナル引数、名前付き引数、nullable変数に対応

class PointB {
  final double x;
  final double y;

  // 位置指定オプション引数
  PointB.opt([this.x = 0, this.y = 0]);

  // デフォルト値付き名前付き引数
  PointB.named({this.x = 5, this.y = 5});
}
Code language: PHP (php)

3:初期化リスト Initializer List

コロン : 後に代入処理を記述。実行タイミングはコンストラクタ本体より早いthis にアクセス不可、assert による引数検証に対応。

class Point {
  final double x;
  final double y;

  Point.fromJson(Map<String, double> json)
      : x = json["x"]!,
        y = json["y"]! {
    print("コンストラクタ本体実行、x=$x");
  }

  // assertによる検証付き
  Point.withAssert(this.x, this.y) : assert(x >= 0);
}
Code language: PHP (php)

11. プライベートフィールド直接初期化引数(Dart3.12以降)

アンダースコア付きプライベート変数に対し、コンストラクタ引数に this._フィールド を記述。外部から呼び出す際はアンダースコアなしの公開名を使用可能。

class Point {
  final double _x;
  final double _y;

  // 内部は _x、外部呼び出し時は x:10 と記述
  Point({required this._x, this._y = 0});
}

void main() {
  final p = Point(x: 10); // _x ではなく x で指定可能
  print(p._x); // 10
}
Code language: PHP (php)

初期化リスト内ではプライベート変数の元の名前を使用:

class Point {
  final double _x;
  Point({required this._x}) : assert(_x > 0);
}
Code language: JavaScript (javascript)

12. 親クラスコンストラクタと継承ルール

  1. サブクラスは親クラスのいかなるコンストラクタも継承しない
  2. コンストラクタ実行順:初期化リスト → 親クラスコンストラクタ → サブクラスコンストラクタ本体;
  3. 親クラスに引数なしデフォルトコンストラクタが存在しない場合、サブクラスは手動で super() を記述し親コンストラクタを指定する必要がある。
基本的なsuper呼び出し例

Javaと同様にsuperで基底クラスを参照、C#ではbaseを使用します。

class Person {
  final String name;
  Person(this.name);
  Person.fromMap(Map map) : name = map["name"];
}

class Employee extends Person {
  int id;
  // 親クラスの名前付きコンストラクタを呼び出す
  Employee(Map data) : super.fromMap(data), id = data["id"];
}
Code language: PHP (php)

注意:superの引数式内では this を使用できません。

Super引数(Dart2.17以降、親クラスへの引数受け渡し簡略化)

super.変数 を使用することで、手動で super(x,y) を記述せず直接親コンストラクタに引数を転送できます。

class Vector2d {
  final double x, y;
  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;
  // super.x super.y が自動的に親コンストラクタに転送
  Vector3d(super.x, super.y, this.z);
}

// 親クラス名前付きコンストラクタ + super名前付き引数
class Vector3dYZ extends Vector2d {
  final double z;
  Vector3dYZ({required super.y, required this.z}) : super.named(x: 0);
}
Code language: JavaScript (javascript)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です