言語編 |
変数はメモリ内の蓄積場所である。
変数にはトップ・レベルに宣言された変数(トップ・レベル変数)、あるクラスに結び付けられた変数(static変数)、及びあるオブジェクトに結び付けられた変数(インスタンス変数)がある。インスタンス変数はあるオブジェクトのフィールド、あるいは属性(property)とも呼ばれる。
2015年5月27日のDart言語幹部たちのDart言語強化提案(DEP)会合で議論された提案である。これはPatrice Chalin氏の提案によるものである。この提案は非ヌル制約型とそれのデフォルト化(Non-null Types and Non-null By Default (NNBD))である。非ヌル制約型によりnullの変数を参照するエラー(いわゆるNPE : null pointer error)を排除し、またJSに変換した際のオーバヘッドを低減できる。これは2.0版として導入される可能性があるとされてきた。しかしながら2.0版になっても導入されることはなかった。これは余りにもインパクトが大きかったこともその原因だったと推察される。
以下はある変数の生成とそれの初期化の例である:
|
変数は参照をストアする。即ち参照渡しである。nameと呼ばれるこの変数は“Bob”という値を持ったあるString型のオブジェクトへの参照を保持している。
nameという変数の型はStringであると推論されるが、その型を別の型を指定して変更することができる。あるオブジェクトが単一の型に制限されていない場合は、Objectまたはdynamic型だと指定する。design guidelineに準拠されたい。
|
別の選択肢としては、それが型推論されるであろう型を明示的に宣言する方法がある:
|
注意:このページではローカル変数に対しては型アノテーションではなくてvarを使うというスタイル・ガイドの勧告に準拠している。
初期化されていない変数はその初期値としてnullを持つ。たとえ数値型の変数であっても初期値はnullである。何故ならDart内のどこにおいても総てがオブジェクトであるように数値もオブジェクトだからである。
|
注意:assert()呼び出しは運用コード(production code)では無視される。開発段階ではassert(condition)はそのconditionがtrueでない限り例外を生起する。
ある変数の値を決して変わらないようにしたいときは、varを使ったり型アノテーションを付加する代わりにfinalまたはconstを使用する。final変数はただ一回セットされる;const変数はコンパイル時常数である。(const変数は暗示的なfinalである)finalなトップ・レベル変数又はクラス変数は、それが最初に使用されるときにおいて初期化される。これをGoogleは後まわしの初期化(lazy initialization)と呼んでいる。プログラムの実行中に初めて生じる初期化のために処理がそこで遅延を起こす可能性が出る。これは処理時間にクリチカルなアプリケーションでは注意が必要である。しかしGoogleにとってクライアント側(ブラウザ上)でのアプリケーションの起動が早いことはそれよりも優先される。Googleの最大の顧客は一般ユーザであって、プログラマではない。
注意:インスタンス変数はfinalであり得るがconstにはなり得ない。finalなインスタンス変数はそのコンストラクタのボディが開始する前…その変数宣言の前に、コンストラクタ・パラメタ、またはそのコンストラクタの初期化リストのなかで、初期化されていなければならない。
以下はfinalな変数の生成と設定の例である:
|
final変数の値を変更することはできない。例えばfinalな変数に'Alice'という値をセットしようとすると以下のエラーがだされる:
|
コンパイル時常数としたいときはその変数にはconstを使用する。もしそのconst変数がそのクラスのレベルにあるときは、static constを使用する。その変数を宣言している箇所では、数値または文字リテラル、const変数、或いは常数値への代数操作の結果といったコンパイル時常数にセットする:
|
constというキーワードは単にコンスタント変数宣言のために使われるためのものではない。これはまた常数値生成、及び常数値を生成するコンストラクタの宣言にも使える。どの変数も常数値を持ちうる。
|
このコードのbaz宣言のように、const宣言の初期化式からconstをオミットできる。
例えそれがconst値を使っていたとしても、非final、非constな変数の値を変えることができる:
|
constやfinal変数の値を変更することはできない:
|
finalなMapでは変更は可能である:
|
finalをconstに変更すると4行目でエラーとなる:
|
常数値生成にconstを使用することに関しては「組込み型の章」のList、Map,及び「Classの章」を参照のこと。
Javaでは値渡しとなるプリミティブは存在せず、Dartでは総てがオブジェクトである。Java同様、Dartでは代入の右辺を計算した結果のオブジェクトが左辺にバインドされる。即ち参照渡しであることに注意されたい。2つの変数間のa=b;という代入はひとつのオブジェクト参照(バインド)していることになる。Stringやnumのような単純なオブジェクトであればその後aやbに代入を行えばそこで両者間の参照関係が切れてしまうので、通常は気にする必要はない。しかしオブジェクトaやbそのものに別のオブジェクトを代入をしない限り、バインド即ち参照は維持される。これはオブジェクトbをラップしたクラスなどに良く使われる。
そのような例として良く使われるのがList、Map、及びクラスのようなオブジェクトである。次の例を考えてみよう:
|
この場合はcopyのオブジェクトはその要素を変更してもoriginalとの参照が維持される。copy["c"] = "Hello copy!";という操作は代入ではなくて追加である。従ってコピーしたほうのcopyにそのような変更を加えれば、それはoriginalにも変更を加えたことになる。
同じことがクラスにおいても言える:
|
copy.b = 2;という操作はcopyというオブジェクトに新たなオブジェクトを代入している訳ではないので、copyはoriginalとの参照を維持し続ける。
上記2つの例でcopyという変数がoriginalとの参照を必ず維持し続ける為にはvar copy やMap copyという記述ではなくて、final copy (あるいはfinal Map copy)と記述するよう習慣づけるべきである。finalな変数は初期化時にそのバインディングが固定される変数である。従ってfinalな変数は初期化後は常に同じオブジェクトを参照する
もうひとつ勘違いしそうな例を示そう:
|
この場合はreassignOneという関数の引数argにはlist即ち[1, 2, 3]オブジェクトへの参照が渡される。しかしながらこの関数の中ではargにはList.from([100, 200, 300])という新しいオブジェクトへの参照が渡される。すなわちここで外のlistとの参照関係が無くなってしまう。従ってreassignOneはlistの中身を変更することにはならない。