はじめに
Dartで同じオブジェクトに対して様々なメソッド呼び出しやプロパティアクセスをたくさんするときに便利なカスケード演算子での私のコメントが長かったので記事として独立させました。
実験
canvas.getContext('2d');
のようにスーパークラス型の関数が、実際にはサブクラスのオブジェクトを返すことがあります。
この場合にDart Editorによる補完がどのように働くか実験してみます。
class A {
int a;
}
class B extends A {
int b;
}
A fa(String t){
if(t == 'A'){
return new A();
} else {
return new B();
}
}
void main() {
var b1 = fa('B')..b = 1; // (1)
B b2 = fa('B')..b = 2; // (2)
var b3 = fa('B') as B..b = 3; // (3)
B b4 = fa('B');
b4.b = 4; // (4)
print('b1.b:${b1.b} b2.b=${b2.b} b3.b=${b3.b} b4.b=${b4.b}');
}
> b1.b:1 b2.b=2 b3.b=3 b4.b=4
解説
(1)のfa(’B')..
のところで、メンバa
とともにb
もグレーアウトされた状態で最後の補完候補として現れます。
ここから、サブクラスのメンバは優先度を下げつつも候補としてリストする方針が見て取れます。
var b1
はdynamic
型ですが、仮にこれを(2)のようにB b2
に変更しても補完候補は変わりません。
fa('B')..
の方がb2 =
よりも先に評価されるため、変数b2
の型情報B
が利用できないのでしょう。
文脈的にサブクラスのオブジェクトとわかっている場合はas
を使ってキャストすると良いようです。
(3)の例ではas B..
のところでa
に続いてb
がグレーアウトせずにリストされました。
なお、個人的には(3)を) as B..b
と書いても、) as B ..b
と書いてもスペースが気持ち悪いです。
加えてvar
とas
が冗長なで、(4)の様に型情報は変数宣言に与えて、そのメンバ参照は文を分けたほうが素直だと思います。
ということで、(4)一択ですかね。
捕捉
- (1)、(2)は動的には正しく、チェックドモードでも完走しますが、エディタやアナライザで警告が出るので非推奨です。
-
fa('B')
をfa('A')
に変更しても補完動作は変わりませんが、当然ながら実行時エラーです。 - Dartのキャスト
as
は基本何もしません。メモリイメージを変更することもなければ、その動的解釈を変えることもありまえん。