👋

Dart NamedConstructorsを公式ドキュメントに基づいて調査する

2024/11/11に公開

https://dart.dev/language/constructors#named-constructors

前提

実務でNamedConstructorを見かけたが何をやっているかイマイチわからなかったので、公式ドキュメントに基づいて調査する。
個人的な目標は、種類の多いコンストラクタを理解しつつ、シングルトンパターンのコードを説明可能な状態にする。
最適な使い方や応用例は今後の実務コードやOSSのコードを読んで探ろうと思うので触れず、あくまで使い方と仕様にフォーカスする。

名前付きコンストラクタとは

Use a named constructor to implement multiple constructors for a class or to provide extra clarity:
名前付きコンストラクタを使用して、クラスに複数のコンストラクタを実装したり、明確さを高めたりします

サンプルコード

const double xOrigin = 30;
const double yOrigin = 70;

class PointWithNameConstructor {
  final double x;
  final double y;

  PointWithNameConstructor(this.x, this.y);

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

void main() {
   final point = PointWithNameConstructor.origin();
  print('${point.x},${point.y}');
}

出力結果


30,70

注意点

1. サブクラスが親クラスの名前付きコンストラクタを継承しない

こちらもドキュメント参照

A subclass doesn't inherit a superclass's named constructor. To create a subclass with a named constructor defined in the superclass, implement that constructor in the subclass.
サブクラス[1]はスーパークラス[2]の名前付きコンストラクターを継承しません。スーパークラスで定義された名前付きコンストラクターを持つサブクラスを作成するには、そのコンストラクターをサブクラスで実装します。

つまり以下のような使い方は出来ない。

const double xOrigin = 30;
const double yOrigin = 70;

class SuperPointWithNameConstructor {
  final double x;
  final double y;
  SuperPointWithNameConstructor(this.x, this.y);
  SuperPointWithNameConstructor.origin()
      : x = xOrigin,
        y = yOrigin;
}

class SubPointWithNameConstructor extends SuperPointWithNameConstructor {
  SubPointWithNameConstructor();
}

void main() {
  final point = SubPointWithNameConstructor.origin(); // originメソッドが存在しないと怒られる
  print('${point.x},${point.y}');
}

2. 一部だけパラメータを変更したい場合

リダイレクトコンストラクタを使う。

まとめ

  • 名前付きコンストラクタを用いることによって、クラスに複数のコンストラクタを実装できる。

OSSで名前付きコンストラクタを使用していた箇所

video_playerのVideoPlayerControllerの定義箇所

https://github.com/flutter/packages/blob/main/packages/video_player/video_player/lib/video_player.dart#L267-L303

BoxConstraintsの内部実装

内部実装

/// See: [constrain], [constrainWidth], [constrainHeight],
/// [constrainDimensions], [constrainSizeAndAttemptToPreserveAspectRatio],
/// [isSatisfiedBy].
class BoxConstraints extends Constraints {
  /// Creates box constraints with the given constraints.
  const BoxConstraints({
    this.minWidth = 0.0,
    this.maxWidth = double.infinity,
    this.minHeight = 0.0,
    this.maxHeight = double.infinity,
  });

  /// Creates box constraints that is respected only by the given size.
  BoxConstraints.tight(Size size)
    : minWidth = size.width,
      maxWidth = size.width,
      minHeight = size.height,
      maxHeight = size.height;

  /// Creates box constraints that require the given width or height.
  ///
  /// See also:
  ///
  ///  * [BoxConstraints.tightForFinite], which is similar but instead of
  ///    being tight if the value is non-null, is tight if the value is not
  ///    infinite.
  const BoxConstraints.tightFor({
    double? width,
    double? height,
  }) : minWidth = width ?? 0.0,
       maxWidth = width ?? double.infinity,
       minHeight = height ?? 0.0,
       maxHeight = height ?? double.infinity;

  /// Creates box constraints that require the given width or height, except if
  /// they are infinite.
  ///
  /// See also:
  ///
  ///  * [BoxConstraints.tightFor], which is similar but instead of being
  ///    tight if the value is not infinite, is tight if the value is non-null.
  const BoxConstraints.tightForFinite({
    double width = double.infinity,
    double height = double.infinity,
  }) : minWidth = width != double.infinity ? width : 0.0,
       maxWidth = width != double.infinity ? width : double.infinity,
       minHeight = height != double.infinity ? height : 0.0,
       maxHeight = height != double.infinity ? height : double.infinity;

  /// Creates box constraints that forbid sizes larger than the given size.
  BoxConstraints.loose(Size size)
    : minWidth = 0.0,
      maxWidth = size.width,
      minHeight = 0.0,
      maxHeight = size.height;

  /// Creates box constraints that expand to fill another box constraints.
  ///
  /// If width or height is given, the constraints will require exactly the
  /// given value in the given dimension.
  const BoxConstraints.expand({
    double? width,
    double? height,
  }) : minWidth = width ?? double.infinity,
       maxWidth = width ?? double.infinity,
       minHeight = height ?? double.infinity,
       maxHeight = height ?? double.infinity;
....省略

その他参考記事

https://dev.classmethod.jp/articles/about_dart_constructors/#item_01-02

脚注
  1. 親クラス(スーパークラス)の継承先のこと ↩︎

  2. サブクラスの継承元のこと ↩︎

Discussion