Dart言語の3つのコンストラクタ
少しずつFlutterに慣れてきたところで、Dart言語も少しお勉強します。
今日はコンストラクタについて。
DartPadというブラウザ上で実行できるREPLがあるので、入力しながら読むと理解がはかどりました。
なお、今日の参考ページはこちら -> Dartのコンストラクタについて - DevelopersIO
クラスの構成
整理すると難しくないのですが、慣れるまで違和感があったので一度整理します。
class Apple { static final String nickname = 'Ann'; // staticをつけるとクラス変数になる String color; // staticをつけないとインスタンス変数。クラス変数と同名は指定できない Person() { // class名()でコンストラクタを記述 this.color = 'Red'; // this.変数名(メソッド名も同様)でメンバにアクセス } }
変数の宣言にはvar, const, finalも使えるし、String, intといった型も指定できます。
ただどちらかは指定しないとコンパイルエラーになるみたい。ふむ。
生成的コンストラクタ(Generative Constructors)
いわゆるふつうのコンストラクタ。
class Person { String name; Person() { this.name = 'Bob'; } } void main() { Person foo = new Person(); print(foo.name); // => 'Bob' }
もちろん引数も取れます。
class Person { String name; Person(String name) { this.name = name; } } void main() { Person foo = new Person('Alice'); print(foo.name); // => 'Alice' }
Automatic field initialization
冗長な感じがするので、代入だけなら簡略して記述できます。
class Person { String name; Person(this.name); } void main() { Person foo = new Person('Tom'); print(foo.name); // => 'Tom' }
複数の引数でもOK。
class Person { String name; int age; Person(this.name, this.age); } void main() { Person foo = new Person('Tommy', 20); print(foo.name); // => 'Tommy' print(foo.age); // => 20 }
Named Constructors
コンストラクタのオーバーロードはできないそうです。
代わりに、コンストラクタに任意の名前を付けて通常のコンストラクタから呼び出す方法を取るみたい。
class Person { String name; String full_name; Person(){ this.name = 'Tommy'; } Person.full_name(){ this.full_name = '${this.name} Smith'; } } void main() { Person foo = new Person(); print(foo.name); // => 'Tommy' print(foo.full_name); // => null Person bar = new Person.full_name(); bar.name = 'Jacob'; print(bar.name); // => 'Jacob' print(bar.full_name); // => 'null Smith' }
うん…?コンパイルは通るけど、こういう使い方じゃない気がする🤔
Redirecting Constructors
オーバーライドしたい時はこれで書くのがよさそう。
class Person { String name; String full_name; Person() : this.name(); Person.name() { this.name = 'William'; this.full_name = '${this.name} Smith'; } } void main(){ Person foo = new Person(); print(foo.name); // => 'William' print(foo.full_name); // => 'William Smith' }
参考ページにはリダイレクト元(左辺)のコンストラクタのボディで処理もできる、とあるのだけど、実行すると「Redirecting constructors can't have a body.」と言われる。
仕様がかわったのかな…?
Initializer Lists
Named Constructorsで、コロンに続けてフィールドの初期化処理を記述できる。
複数フィールドの初期化時はカンマ区切りでリストアップできる。
class Person { String name; String full_name; Person() : this.full_name(); Person.full_name() : this.name = 'Ethan', this.full_name = 'Ethan Smith'; // field initializerの中からthisにアクセスはできない // これはコンパイルエラー // Person.full_name() : this.name = 'Ethan', // this.full_name = '${this.name} Smith'; } void main() { Person foo = new Person(); print(foo.name); // => 'Ethan' print(foo.full_name); // => 'Ethan Smith' }
親のコンストラクタを呼び出す時はリダイレクトコンストラクタを使う。
class Person { String name; String address; Person(); Person.name_address(name, address){ this.name = name; this.address = address; } } class Customer extends Person { Customer() : super.name_address('Emily', 'Tokyo'); // コンストラクタのボディで親クラスのコンストラクタは呼び出せない // Customer(String name, String address){ // super.name_address('Emily', 'Tokyo'); // } } main() { Customer foo = new Customer(); print(foo.name); // 'Emily' print(foo.address); // 'Tokyo' }
ファクトリ(factory)
factory
コンストラクタ。
初めて聞いたのですが、インスタンスを生成しないコンストラクタなので、自分でインスタンスを生成します。
singletonパターンの実装なんかに使うみたいです(あまりよくわかってない)。
class Person { String name; static var _instance; // 返すインスタンスはクラス変数にする factory Person(String name){ if (_instance == null) { _instance = new Person._internal(name); } return _instance; } Person._internal(this.name); } void main(){ Person foo = new Person('Alan'); Person bar = new Person('Elen'); print(foo.name); // => 'Alan' print(bar.name); // => 'Alan' print(foo == bar); // => true、同じインスタンスを返している }
定数コンストラクタ(Constant Constructors)
コンパイル時に定数オブジェクトをインスタンス化したい場合に使うそう。
グローバルスコープに定数オブジェクトを定義したい時、みたいな感じ…?🤔
定数オブジェクトのフィールドはfinal
、コンストラクタはconst
で定義、インスタンスの作成もconst
で行います。
ちなみにconst
とfinal
はどちらも定数を宣言するキーワードっぽいのだけど、以下のような違いがあるそうです。
final
: 代入が1度(宣言時)のみ、値が参照値の場合も変更不可const
はコンパイル時に値が決まる定数(暗黙的にfinalとなる)
Final and const - A Tour of the Dart Language
class Person { final name; const Person(this.name); } final foo = const Person('Colin'); void main(){ final Person bar = new Person('Mike'); print(foo.name); // => Colin print(bar.name); // => Mike }
以上、コンストラクタのまとめでした!