Dart
https://cdn.arstechnica.net/wp-content/uploads/2015/05/dart-logo-wordmark-640x170.jpg
install
目的ごとにインストールする処理系が異なる
サーバーサイド、フロントエンド → Dart SDK
モバイルアプリ → Flutter
DartSDK で入るもの
lib/以下は標準ライブラリ
table:/bin以下に入るもの
コマンド名 説明
dart The standalone VM
dart2js The Dart-to-JavaScript compiler (used only for web development)
dartanalyzer The static analyzer
dartdevc The Dart development compiler (used only for web development)
dartdoc The API documentation generator
dartfmt The Dart code formatter
pub The Dart package manager. Pub also includes commands for creating, developing, running, and deploying Dart apps.
pubspec.yaml
複雑な Dart プログラムに必要になる pub 向けの定義ファイル
pub run などのコマンドに必要
典型的な pub向けのディレクトリ構造
https://www.dartlang.org/tutorials/dart-vm/images/pub-directory-structure.png
bin には main() を含む .dart ファイル
lib にはその他の必要な .dart ファイル
pubspec.yaml に pub定義ファイル
$ pub run bin/main.dart
言語仕様など
型注釈は前置
関数をトップレベルで定義可能
class, Objectに内包されなくても良い
トップレベル関数の定義は dart:core
短い関数は => でも定義可能
ファイル名は snake_case が慣習 hoge_fuga.dart
var で型推論的代入
Dart のコンセプト
変数に格納されるものは全てclass のインスタンスである object
リテラル、null, 関数も全てオブジェクト
全ての class は Object を継承している
型推論があるので、型注釈はオプション扱い
記載しなくても推論して型が定まる
明示的に型を指定したくないときは dynamic 型注釈を付ける
ジェネリックをサポートする
List<int>
List<dynamic> → 全ての型が入るリスト
トップレベル関数のサポート (not java like)
_hoge な名前付けは private を意味する
式(expression)と文(statement)の区別について曖昧なまま進めないこと
変数
変数はオブジェクトへの参照を保持する
全ての変数のデフォルト値は null
code:var.dart
var name = 'Bob'; // 型推論の代入 (Go でいう name := "Bob") ローカル変数宣言は var を推奨
dynamic name = 'Bob'; // 動的型変数
String name = 'Bob'; // 型注釈による明示
const & final
変更しないつもりの変数には var や型注釈の代わりに const, final を付ける
const はコンパイル時定数
コンパイル時に値が必要、要は事前に定義される定数
暗示的に final
final は一度値を設定すると変更不可
コンパイル時に値が定まっていなくても良い
基本型
int, double は num のサブタイプ
num 型に定義されていない数値演算は dart:math から探すこと
code:num.dart
// 文字列から数値
var one = int.parse('1');
var onePointOne = double.parse('1.1');
// 数値から文字列
String oneAsString = 1.toString();
String piAsString = 3.14159.toStringAsFixed(2);
文字列は UTF-16コードユニットで "", '' で定義可
コードユニットについては以下の特徴の項を参照のこと
UTF-16では1バイトが16ビットになるが、これをバイトと呼ぶと混乱を招くため、UTF-16ではこれをコードユニットと呼ぶ。
"${}" で式の結果の表示可能
複数行文字列 → """ ほげふが """
Raw文字列 → r"\n でも改行されない"
真偽値
Dart の型安全のため if (nullableVar) のような書き方はできない
型推論によるチェックのため、classに紐づくメソッド呼び出しが必須のようだ
code:dart_bool_type.dart
// 空文字列チェック
var fullName = '';
assert(fullName.isEmpty); // isEmpty プロパティを呼び出さないとダメ
Map
Pythonでいう辞書
Map<int, String> という型なら整数がキーで文字列が値
存在しないキーへのアクセスは null が返る (pythonみたく KeyError例外とか出ない)
assert(map['wrong_key'] == null);
Runes (ルーン)
文字列のUTF-32コードポイント
コードポイント ≠ コードユニットなので注意
表現方法 → \uffff
32bit を超えるとき → \u{1f44f} → 👏
UTF-16はサロゲートペアで文字を表現したときに32bitになることがあるため UTF-32
サロゲートペアによって拡張された符号位置は、UTF-32ではそのまま表現できる。 from Wikipedia
Symbols
Dartプログラム内の演算子や識別子を表す
# で定義する → #hoge
Functions (関数オブジェクト型)
=> は { return expr; } の省略形
far arrow syntax と呼ぶ
名前付き引数 → {name: 型} とする
code:named_arg.dart
int add({a: int, b: int}) => a + b;
add(a:1, b:1) // -> 2
デフォルト引数 → {name: 値} とする
code:default_arg.dart
int add({a = 1, b = 2}) => a + b;
add() // -> 3
add(a: 3) // -> 5
位置によるオプション引数 → [] で囲む
code:optional_arg.dart
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
無名関数
(args) { print(args) }
型比較
val is SomeType → val が SomeType 型かチェック
is! で否定
as でタイプキャスト
(emp as Person).name = 'Bob' → emp が null とかだったら例外発生
カスケード表記 ( .. )
同一のオブジェクトに対して操作を連続して行う
F#, elixir の |> とは違うので注意
() でネストするとオブジェクト内のオブジェクトに対してもカスケード表記可能
code:cascade_notation.dart
querySelector('#confirm') // オブジェクト取得
..text = 'Confirm' // オブジェクト.txt に 'Confirm' を設定
..classes.add('important') // オブジェクトに class追加
..onClick.listen((e) => window.alert('Confirmed!'));
例外処理
try .. on で例外処理、 catch(err, stack_trace) で例外オブジェクトとスタックトレースを取得できる
try .. on の中で rethrow すると同じ例外を再度投げれる
例外処理を分割したいときに便利
finally で最終処理
code:exception.dart
try {
process();
} on OutOfLlamasException {
// 特定の例外タイプをキャッチ
some_error_handle();
} on Exception catch (e) {
// その他の例外オブジェクトを e として取得する
print('Unknown exception: $e');
} catch (e) {
// 全ての例外を e としてキャッチして、同じ例外を再度投げる
print('Something really unknown: $e');
rethrow;
} finally {
print('終わり!')
}
Class
runtimeType プロパティで、型を取得可能
Dart ではクラスを継承してもコンストラクタは継承されない
名前付きコンストラクタも同様
コンストラクタ未定義の Class には 引数なし無名コンストラクタ が呼ばれる
コンストラクタの初期値代入の糖衣構文 → Point(this.x, this.y); // this. で代入処理と同じ効果
クラス継承についてのわかりやすいまとめ
インスタンス生成時のコンストラクタ呼び出し順
1. initializer list
2. 親クラスの引数なしコンストラクタ
3. 自クラスの引数なしコンストラクタ
初期化子リスト(initializer list)
コンストラクタが呼ばれる前にインスタンス変数の初期化が可能になるのが initializer list
正直、使い所がわからない…
普通のコンストラクタ内で this.x = x すればいいのでは?
code:initilizer_list.dart
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
print('In Point.fromJson(): ($x, $y)');
}
// 開発中にアサーションを仕込むことも可能
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
リダイレクトコンストラクタ
コンストラクタから別のコンストラクタへ動作を移動する
code:ridirect_constructor.dart
class Point {
num x, y;
Point(this.x, this.y); // メインのコンストラクタ
Point.alongXAxis(num x) : this(x, 0); // this でメインコンストラクタへ動作を移譲
}
Factoryコンストラクタ
インスタンスを生成して返す必要があるコンストラクタ
必ずしも新しいインスタンスを生成せずクラス内のstaticなキャッシュを返したりする
Factoryコンストラクタは this へアクセスできない
code:factory_constractor.dart
class Logger {
final String name;
bool mute = false;
// クラスのstaticプライベートプロパティ
static final Map<String, Logger> _cache = <String, Logger>{};
// ファクトリコンストラクタ
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cachename; // キャッシュを返す } else {
final logger = Logger._internal(name);
return logger; // キャッシュに登録して返す
}
}
Logger._internal(this.name); // プライベートコンストラクタ
}
インターフェース
Class定義は自動的にインターフェースを定義する
implementsキーワードを使う → class Impostor implements Person
同じインターフェースを満たすClassは共通の型といて関数の引数に渡せる
ジェネリクス
型変数は慣習的に英語大文字1文字 → <T>
すべてのDartアプリは library ディレクティブを使用していなくてもライブラリとなる。
非同期処理
async は Future オブジェクトを返す関数
await が付いた関数呼び出しが現れるまで実行されることはない
プログラム全体は await 呼び出しが完了するまで待ち、その後 Future が返る
つまり await 式は、そのオブジェクトが利用可能になるまで実行を一時停止する
asyncが付いた関数は自動的にオブジェクトを Futureでラップする
Future の使い方
code:future_basic.dart
HttpRequest.getString(url).then((String result) {
print(result);
}).catchError((e) {
// Handle or ignore the error.
});
// これは以下と等価
try {
var result = await HttpRequest.getString(url);
print(result);
} catch(e) {
// ...
}
複数の非同期処理を待つ
code:await_multi.dart
Future deleteLotsOfFiles() async => ...
Future copyLotsOfFiles() async => ...
Future checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
Typedefs
関数型エイリアス
型に別名を付ける
code:typedefs.dart
// Compare は (Object a, b) -> int の関数を表す
typedef Compare = int Function(Object a, Object b);
// 以下の省略記法もあるようだ(分かりづらい…)
// これの意味 → typedef LoggerOutputFunction = void Function(String msg)
typedef void LoggerOutputFunction(String msg);
印象など
公式サイトのドキュメントから、モダンなIDE、エディタでの開発が前提のような雰囲気
Class のコンストラクタの仕組みが他の言語とだいぶ異なるため、注意が必要
参考資料
strictモード(のようなもの)の設定
標準ライブラリツアー