A tour of the Dart languageを読んだ
ので、気になったところをピックアップしていく。
Basics
printInteger(int aNumber) { print('The number is $aNumber.'); } main() { var number = 42; printInteger(number); }
main
はエントリポイント。
行の末尾にはセミコロン必須の模様。これはイマドキっぽくない感じ。
メソッド定義にはfunc
などのキーワード不要。
$aNumber
で変数展開ができる。${aNumber}
でもOK
var
で型を指定せず変数の宣言。イミュータブルにしたい場合let
ではなくfinal
を使うみたい。
Important concepts
全てはオブジェクト。全てのオブジェクトはクラスのインスタンス。全てのオブジェクトはObject
クラスを継承している。
Dartでは型のアノテーションは必須ではなく、型推論が使える。型が決まらないものはdynamic
として扱える。
Generic型がある。List<int>
とかで宣言できる。
top-levelな関数と変数の概念がある。main()
はtop-level func。
Javaとは違い、public
, protected
やprivate
というキーワードはない。
_
から始まると、privateとして扱われる。
default value
初期化されていない変数はnull
を初期値とする。optionalは無い。まじ?
Final and const
もし変数を変更したくなかったら、final
かconst
を使うと良い。
const
はコンパイル時に値が決定する。final
は再代入できない。
Maps
ハッシュ。
var gifts = { // key: Value 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }; var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', }; var gifts2 = Map(); gifts2['first'] = 'partridge';
Symbols
SymbolはDartのプログラムで宣言されているoperatorやidentifierを表すオブジェクト。 普通に生きてる限りは使うことにはならないらしい。
#
に続けてidentifierを書くことで、Symbolを取得できる
#radix #bar
Functions
functionはFunction
型のオブジェクト。変数に代入したり、引数として渡したりできる。
引数に名前をつけると、その引数は必須ではなくなる。
enableFlags(bold: true, hidden: false);
もし必須にしたかったら、@required
属性をつけると実現できる。
ちなみにmeta
パッケージが必要らしいと書いてあるが、意識したことがない。変わってるかも。
package:meta/meta.dart
const Scrollbar({ Key key, @required Widget child})
なお、ここには書いてなかったが、Protectedを表す@protected
オペレータを使う場合にはmeta.dart
のインポートが必要だった。
Anonymous functions
無名関数が定義できる。
(int hoge) { /* do something */ }; // 1行ならこう書ける (hoge) => print(hoge);
Optional parameters
{}
で囲んだパラメータは必須ではなくなる。
/// Sets the [bold and [hidden flags ... void enableFlags({bool bold, bool hidden}) {...}
が、@required
をつけたパラメータは必須になる。
const Scrollbar({Key key, @required Widget child})
optionalとnamedがごっちゃになってるので正直ちょっと使いづらい。
Type casts
as
, is
, is!
が使える。
as
はキャスト、is
とis!
は型が一致するか調べる。
if (emp is Person) { emp.firstName = 'Bob'; } (emp as Person).firstName = 'Bob';
この辺まで読んだ感想
- そこはかとなく漂うレガシー感
- JavaScriptよりはマシ?でもダックタイピングができないのにインターフェイスがないのは正直つらい
cascade notation
..
という記法。
同一のオブジェクトにメソッドを続ける時に使う?これも使ったことない。普通に.
で繋げられる。
querySelector('#confirm') // Get an object. ..text = 'Confirm' // Use its members. ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
Classes
obj.runtimeType
で実行時の型を知ることができる。
print('The type of a is ${a.runtimeType}');
返り値はType型のオブジェクト。
あくまでType型なので、レシーバのクラスオブジェクトを取得できるわけではない。
Constructors
クラス名がそのままコンストラクタになる。 コンストラクタのパラメータをプロパティにセットするだけならシンタックスシュガーで書くと良い。
class Point { num x, y; // Syntactic sugar for setting x and y // before the constructor body runs. Point(this.x, this.y); }
- 他にも色々
Named Constructor
class Point { num x, y; Point(this.x, this.y); // Named constructor Point.origin() { x = 0; y = 0; } }
User.fromJson
のように、コンストラクタにラベルをつける感じで使われることが多いみたい。- Swiftの
init(from json: Any)
っぽいと思った
- Swiftの
Invoking a non-default superclass constructor
サブクラスはコンストラクタが呼ばれた時、自動でスーパークラスのコンストラクタを呼ぶ。
呼ばれるタイミングは、サブクラスのコンストラクタの中身に到達する前。
initializer list
が定義されていた場合、そちらが優先して呼ばれる。
呼ばれる順番は以下の通り。 1. initializer list 2. superclass’s no-arg constructor 3. main class’s no-arg constructor
親クラスがunnamed
なコンストラクタを持っていない場合、親クラスのコンストラクタのどれを呼ぶかはサブクラスのコンストラクタで決めないといけない。
class Person { String firstName; Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { // Person does not have a default constructor; // you must call super.fromJson(data). // :の後に、親クラスのコンストラクタを指定する。その後に続くブロックの中身はサブクラスのコンストラクタ Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } } main() { // Employeeの名前付きコンストラクタを呼ぶ var emp = new Employee.fromJson({}); // Prints: // in Person // in Employee if (emp is Person) { // Type check emp.firstName = 'Bob'; } (emp as Person).firstName = 'Bob'; }
実行結果は以下の通り。
in Person in Employee
initializer list
親クラスのコンストラクタを呼ぶ代わりに、インスタンス変数をコンストラクタのBodyに到達する前に初期化できる。
initilizerは,
で区切ることができる。
// Initializer list sets instance variables before // the constructor body runs. Point.fromJson(Map<String, num> json) : x = json['x', y = json['y' { print('In Point.fromJson(): ($x, $y)'); }
Development環境では、initializer listにassertメソッドが使える。
Point.withAssert(this.x, this.y) : assert(x >= 0) { print('In Point.withAssert(): ($x, $y)'); }
Redirecting constructors
class Point { num x, y; // The main constructor for this class. Point(this.x, this.y); // Delegates to the main constructor. Point.alongXAxis(num x) : this(x, 0); }
Swiftのconvenience init
的に使える。
Factory constructors
factory
キーワードを使って、クラスのインスタンスを生成しないコンストラクタを作れる。
例えば、コンストラクタからキャッシュやサブタイプを返す場合に使える。
よくJSONからモデルを生成するときにModel.fromJSON
を生やす。
class Logger { final String name; bool mute = false; // _cache is library-private, thanks to // the _ in front of its name. static final Map<String, Logger> _cache = <String, Logger>{}; factory Logger(String name) { return _cache.putIfAbsent( name, () => Logger._internal(name)); } // privateなコンストラクタ Logger._internal(this.name); void log(String msg) { if (!mute) print(msg); } } var logger = Logger('UI'); logger.log('Button clicked');
おわりに
言語仕様に不満はあるものの、Flutterの開発体験が良いのでトータルでプラスという印象。 JavaScriptの代替としてはTypeScriptのほうが優秀かな...。