Dartは非常にシンプルな言語であるが、恐らく初心者が、誤って無限ループに陥ると思われる箇所がいくつかある。
ファクトリーコンストラクター
class X { factory X() => new X() ; // 無限ループ } void main() { X x = new X() ; }
このコードは、無限ループに陥る。なぜならば、new X()というのは、ファクトリーコンストラクターXを呼び出す式である。これは、つまり自分自身を再帰呼び出ししていることになる。結果として、無限ループになる。
正しいファクトリーコンストラクターの書き方は、別のコンストラクターを呼び出すものである。
class X { X.internal() { } factory X() => new X.internal() ; }
クラスのゲッターとセッター
クラスのゲッターとセッターは、クラスの変数への簡単な読み書きを提供するための特殊なメソッドである。これは、暗黙に生成される。
class X { int val = 0 ; // 以下のようなゲッターとセッターが暗黙的に生成される // int get val() => val ; // void set val( int value ) { val = value ; } } void main() { X x = new X() ; x.val ; // ゲッター呼び出し x.val = 0 ; // セッター呼び出し }
ご覧のように、セッターとゲッターは関数であるが、特別な文法で呼び出すことができるのだ。
サブクラスでは、この暗黙のゲッターとセッターをオーバーライドすることができる。つまり、ゲッターとセッターで、なにか複雑な処理を行うこともできるのだ。
class X { int val = 0 ; } class Y extends X { int get val() => val ; // 無限ループ }
これは無限ループとなる。なぜか。考えてみて欲しい、クラスYのスコープにおけるvalとは何なのか。それは、もちろんYのスコープで宣言されているvalである。つまり、Yのゲッター関数valということになる。これは、自分自身の再帰呼び出しである。つまり、無限ループになる。
正しいサブクラスによるゲッターとセッターのオーバーライドは、スーパークラスのゲッターとセッターを呼び出すものである。
class X { int val = 0 ; } class Y extends X { int get val() => super.val ; }
これで、再帰呼び出しは起こらない。
ちなみにいうと、ゲッターとセッターはトップレベル関数でも使える。
int _val = 0 ; int get val() { print("このままでは菌類が死滅してしまう") ; return _val ; } void set val( int value ) { print("勉強でも仕事でも 楽しんでやったものが、一番自分の力になるものさ。") ; _val = value ; } void main() { int x = val ; val = 1 ; }
VBのProperty GetとLetみたいでわかりやすいですね。
ReplyDeleteC++にもこれがほしい。