17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-07-29

1. コンストラクタとは

Classのインスタンスを作成したとき(newしたとき)に呼び出される初期化処理です。

2. Generative Constructors(生成的コンストラクタ)

通常、classをnewしたときに呼び出されるコンストラクタ

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)

class Point {
  double x;
  double y;

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

ちなみに、finalを設定している変数(finalはtsのconst相当)は最初に示した初期化方法が使えません。
https://doitu.info/blog/5c10f5358dbc7a001af33ce5

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(後述)での初期化であればできるとのこと

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);
}
class Point {
  final double x;
  final double y;

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

3. Named Constructors

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

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

コンストラクタの処理を他のコンストラクタにリダイレクトすることができます。

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 = ...と続きます。

class Point {
  double x;
  double y;

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

よくFlutterで出てくる、以下のようなコンストラクタもInitializer Listに含まれるとのこと。(Redirectではないらしい・・。)
MainContainerPageの初期化前に親コンストラクタを実行しています。

class MainContainerPage extends StatefulWidget {
  MainContainerPage({Key? key}) : super(key: key);
  ...
}

もしサブクラスでも何か初期化をしたい場合は{}を続けて記載するそうです

class MainContainerPage extends StatefulWidget {
  MainContainerPage({Key? key}) : super(key: key) {
    // サブクラスの初期化処理を書く
  }
  ...
}

6. Factories(ファクトリ)

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


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);
  }
}

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


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(定数コンストラクタ)

コンパイル時に定数オブジェクトをインスタンス化するためのコンストラクタ。
同じ定数を引数とする場合、何度生成しても同一のインスタンスとなる


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省略※とは異なるので注意する。

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

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


// ここでは多くの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

17
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?