👋
Dart NamedConstructorsを公式ドキュメントに基づいて調査する
前提
実務で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の定義箇所
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;
....省略
その他参考記事
Discussion