Dart言語を勉強しようと思い立った方の中には、React Nativeからの延長で学習を始める人も多いのではないでしょうか。JavaScriptでのClassの書き方が、Dartではちょっと違ったりしますね。
Dartのコンストラクタの書き方やインスタンスからの呼び出し方などを、JavaScriptと比較しつつ紹介していきます。Dartは名前付き引数が使えるので、エラー周りの回避など非常に便利です。
最も基本的なDartのクラスのコンストラクタサンプルコード
以下にDart言語の基本となるクラスのスニペット的なコードを掲載します。
このPerson(this.age, this.name);
がコンストラクタになりますね。
class Person {
int age;
String name;
Person(this.age, this.name);
String toString() {
return '$name : $age';
}
}
JavaScriptでは次のように記述します。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
_toString() {
return this.name + " : " + this.age;
}
}
この2つに共通するのは、インスタンス側で引数を渡す必要があります。ただ、JavaScriptではインスタンス化する際に引数を渡さなくてもエラーにはなりません。name, ageともに引数を空にした状態で実行するとundefinedが格納されます。次のJavaScriptのコードを見てください。
var ps = new Person('Anemone');
console.log(ps._toString());
上の例では、Personクラスに引数を1つしか渡していません。これを実行すると、コンソール上でAnemone : undefined
と表示され、特にエラーが吐かれることはありません。
しかしDart言語ではClassのコンストラクタの引数と実行側の引数が合わないとエラーになります。
void main(List<String> arguments) {
Person submod1 = Person("anemone");
print(submod1.toString());
}
上記のように記述すると、VSCodeではエディタ上で2 positional argument(s) expected, but 1 found. Try adding the missing arguments.
というエラーが出ますが、これを無理やり実行すると
: Error: Too few positional arguments: 2 required, 1 given.
bin/dart_application_1.dart:4
Person submod1 = Person("anemone");
というエラーがターミナルに出力されます。arguments
は日本語で引数を意味します。2つ必要だけど1つしか与えられてないよ、というエラーですね。
「List<String> arguments」の意味
話は変わって、蛇足になるかもしれませんが、先ほど実行したmain()の中にList<String> arguments
という引数が指定されています。この引数は、コマンドライン引数(command line arguments)と呼ばれるもので、ターミナルからDartアプリケーションを実行する際、オプションを付けて実行する場合に活躍します。
前述のmain()を少し改造してみます。コマンドライン引数を出力するようにしてみました。
void main(List<String> arguments) {
Person submod1 = Person(14, "anemone");
print(submod1.toString());
print(arguments); // 追加
}
これをオプション無しでコマンドラインから実行(dart)すると、
anemone : (14)
[]
と出力されます。この「[]」というのはオプション(コマンドライン引数)が指定されていないので空のListが出力されます。
今度はターミナルから以下のコマンドを実行してみます。
dart ./bin/dart_application_1.dart --test sample -5goki hades
--test sample -5goki hades
の部分がオプションですね。React Nativeをやってる方は、Node.jsをインストールするときに使用されたかと思います。
すると、
anemone : (14)
[--test, sample, -5goki, hades]
と、コマンドラインでオプションとして、指定した文字列が引数として扱われ、Listの中に入りました。
もし、このオプションをmain()内で処理する必要がある場合には、このままでは使えないのでご注意ください。「args」というDartのパッケージを利用する必要があります。ここでは割愛しますので、気になる方は、「Dart args」などでWeb検索をしてみてください。
Dart言語の魅力の1つ、名前付き引数を使う
最初に紹介したDart言語のクラスの例をそのまま使うことは、残念ながら出来ません。では、名前付き引数を使えるようにClass側を少し修正してみましょう。
class Person {
int age;
String name;
Person({required this.age, required this.name});
String toString() {
return '$name : ($age)';
}
}
パッと見、どこが違うか分かりますでしょうか。1か所?2か所?たぶん見落としてしまう方がいる可能性が高めなので、コンストラクタだけ抜き出して説明しますね。
Person({required this.age, required this.name});
1個目、requiredキーワードが付与されていることが分かると思います。requiredキーワードなしでコードを書くとVSCode先生に「つけなさい」と怒られます。
他サイトさんの情報見てると、requiredが付いてないことが非常に多かったため、もしかしたら最近仕様に変更があったのかもしれませんね。というか、引数つけてるのに明示的にrequiredキーワードを記述しないとダメなのって腑に落ちない気も……
2点目、これ見逃すかたかなり多いんではないでしょうか。正解は、引数の中に「{}(波括弧、中括弧、ブレース)」でくくってありますね。この波カッコが無いと、呼び出し側のmain()の中に名前付き引数をしていしても、Dartは引数を認識せず、「引数ゼロだよ」というエラーを吐きます。
main()のインスタンス化の際に引数を名前を定義して渡すことが出来ます。文章だとややこしいのでサンプルソースをどうぞ。
void main(List<String> arguments) {
Person submod1 = Person(age: 14, name: "anemone");
print(submod1.toString());
}
age: 14, name: "anemone"
のように「引数名: 値」を「,(カンマ)」で区切ってあげます。この引数内の順序は不問で、「age: 14」が先でも後でも出力される結果は同じものになります。引数の名称(キー?)が合ってさえいれば、引数の順序を守らなくていいので、メンテもイメージも少し楽になりますね。
まとめ