Dartのプログラミングについて備忘としてメモ書きしています。
※目次をクリックすると目次の下部にコンテンツが表示されます。
- 1.Dartの概要
- 2.変数の概要
- 3.ビルトインの型
- 4.関数の概要
- 5.演算子の概要
- 6.制御フロー文の概要
- 7.例外の概要
- 8.クラスとオブジェクトの概要
- 9.インスタンス変数の概要
- 10.コンストラクタの概要
- 11.メソッドの概要
- 12.abstract、インタフェースの概要
- 13.クラスの拡張
- 14.列挙型
- 15.ミクスイン
- 16.クラス変数とクラスメソッド
- 17.総称型
- 18.Dartのライブラリの概要
- 19.非同期のサポート
- 20.typedef、関数型エイリアスの概要
- 21.メタデータの概要
- 22.パッケージ管理の概要
- 23.DartでMySQLに接続
(1)Dartの概要、目的
・V8として知られているChromeブラウザのJavaScriptエンジンの担当者がDartの開発の中心になっている。
・Webプラットフォームの進歩と比べて開発者の経験はそれほど向上していない。JavaScriptエンジンの性能が向上してもWebアプリの起動は依然として遅い。
・高パフォーマンスを目指す。Dartの開発者はVMのエンジニアで、高性能にビルドする方法を知っている。より構造化された言語は最適化が容易で、VMや起動の高速化を可能にする。
・生産性向上を目指す。ライブラリとパッケージによってコードの再利用、流用、共用が可能。Dartの型づけは、APIをクリアにし、使用しやすくする。リファクター、ナビゲート、コードのデバッグに便利なツールがある。
(2)Dartの特徴
・オープンソース、構造化されたHTML5ウェブアプリを作成するプラットフォーム。
・言語だけでなく、ライブラリ、エディタ、仮想マシン(VM)、Dartアプリを実行できるブラウザ、JavaScriptへのコンパイル環境も含む。
・クラスを使ったオブジェクト指向言語、単一継承、lexical scope、トップレベル関数、なじみのある構文。
※lexical scope
識別子のスコープがコンパイル時に識別子宣言を含む最小のブロックに限定されるもの。
・JavaScriptにコンパイルできるので大半の新しいWeb環境で実行できる。
・クライアントだけでなくサーバー環境でも実行できる。
・DartのVMはWebブラウザに統合できるが、コマンドラインでスタンドアロンでも実行できる。
・ビルトインのライブラリは、ファイル、ディレクトリ、ソケット、Webサーバーなどをサポートしていて、end-to-endアプリのあらゆる場所で利用できる。
・Dartでは、動的型付けに必要に応じて静的型づけを付加できる。
・Dartは、小さなスクリプトから大規模で複雑なアプリまでiterativeに作成できる。リロードボタン押下がコンパイラーのように作用し、スピーディーに開発できる。
・トップレベル関数、オプションの型づけ、クラスとライブラリがサポートされているので、小さなスクリプトから開始し、追加していくことが簡単に出来る。
・安全でシンプルなアイソレートによる並行処理をサポート。従来の共有メモリスレッドはデバッグが困難でデッドロックが発生しやすかったが、軽量、高速で安全であることが考慮されている。
・Dartはコードの共有がサポートされている。従来のWebプログラミングワークフローでは、任意のソースやフレームワークからサードパーティのライブラリを統合できなかった。
(3)Dartの重要なコンセプト
●オブジェクト
・変数内に割り当てる事ができるのはすべてオブジェクト。そして、すべてのオブジェクトは、クラスのインスタンス。
・数値、関数、nullもオブジェクト。
・すべてのオブジェクトは、Objectクラスを継承する。
●型づけ
・下記のように明示的に型を指定すると意図が分かり、ツールに静的チェッカも可能となりメリットが大きいが、静的な型づけはオブション。
String name = ‘Bob’;
・デバッグ中に明示的に型を指定していない場合”dynamic”と表示される。
●実行前にすべてのコードをパース
・Dartは実行前にすべてのコードをパースする。
・明示的に型を指定したり、コンパイル時定数を設定するなどDartにtipsを与えることによって、エラーをキャッチしたり、コードを早く実行する手助けとなる。
●トップレベル関数、トップレベル変数
・main()のようなトップレベル関数、クラスやオブジェクトに結びづけられた関数(スタティックメソッド、インスタンスメソッド)をサポートする。
・関数内に関数を差作成できる(ネストされた関数、ローカル関数)
・トップレベル変数クラスやオブジェクトに結び付けられた変数(スタティック変数、インスタンス変数)。
・インスタンス変数は、フィールドまたはプロパティとも呼ばれる。
●プライベート
・public、protected、privateのキーワードは使用しない。
・アンダースコア(_)で始まる識別しは、そのライブラリにprivateである事を意味する。
●productionとcheckedのランタイムモード
・開発とデバッグ中はcheckedモードでproductionモードでデプロイすることを推奨。
・デフォルトはproductionモードで、速度が最適化される。productionモードではassertステートメントと静的な型は無視される。
・checkedモードでは、型エラーもチェックし例外をスローする。
var name = ‘佐藤’;
・変数を作成し、値を割当て。
・変数は参照。上記例では、nameは’佐藤’という値を持つ文字列オブジェクトへの参照。
2)変数のデフォルト値
・初期化されていない変数は、初期値としてnullを持つ。
・数値型でもオブジェクトなので初期値としてnullを持つ。
3)オプションの静的型づけ
オプションの静的型づけを使用する場合は、下記のように記述する。
String name = ‘佐藤’;
4)final、const
・変数を変更しない場合は、varではなく、finalまたはconstを使って宣言する。
・finalは値がセットされるのが一度のみで、constはコンパイルタイム定数という違いがある。
●final
・finalとして宣言されたトップレベルまたはクラス変数は、最初に使用されるときに初期化される。
このLazyな初期化によってアプリの開始速度が速くなる。
●const
・const変数がクラスレベルの場合は、static const ~と宣言する。
(1)ビルトインの型
・numbers、strings、booleans、lists、maps、symbolsをサポートしている。
・Dartではすべての変数はオブジェクトを参照しているので、変数を初期化するのにコンストラクタを使用出来るビルトインの型もある。
例)new Map()
(2)Numbers
・numとそのサブタイプとしてintとdoubleがある。
・多くの演算子がnumに含まれていて、sin, cos, pow, min, maxなどはlibrary:mathに含まれている。
1)int
整数。-2の53乗~2の53乗。
例)
var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 34653465834652437659238476592374958739845729;
2)double
64ビットの浮動小数点。
例)
var y = 1.1;
var exponents = 1.42e5;
3)num
+、-、/、*、>>などのビット演算子、abs()、ceil()、floor()なども含む。
4)文字列との変換例
①文字列からint
var one = int.parse(‘1’);
②文字列からdouble
var onePointOne = double.parse(‘1.1’);
③intから文字列
String oneAsString = 1.toString();
④doubleから文字列
String piAsString = 3.14159.toStringAsFixed(2);
(3)String
・UTF-16の文字列。
・シングルクォートまたはダブルクォートで定義。
●文字列内挿入、結合
・文字列内に${expression}の書式を使って式の値を挿入する事が出来る。識別子の場合は、波括弧を省略できる。
・+オペレータまたは隣り合わせて記述して、結合する事が出来る。
例)
var s = 'string interpolation'; var s1 = 'Dart has $s, which is very handy.'; var s2 = 'That deserves all caps. ' + '${s.toUpperCase()} is very handy!';
・3重のシングルまたはダブルクォートで複数行の文字列を作成できる。
例)
var s1 = ”’
You can create
multi-line strings like this one.
”’;
var s2 = “””This is also a
multi-line string.”””;
●ユニコードエスケープ
文字列内にユニコードエスケープを使用する事が出来る。
例)
print(‘Unicode escapes work: \u2665’);
●生(raw)文字列
rをプレフィックスとしてつけると生文字列を作成できる。
例)
var s = r”In a raw string, even \n isn’t special.”;
(4)Boolean
・”true”のみがtrueとして扱われる。 1、非nullのオブジェクトまたは非ゼロの数をtrueとして取り扱われるJavaScriptなどとは異なるので注意。
(JavaScript) var name = 'Tom'; if(name) { print(name); } (Dart) var name = 'Tom'; if(name != null && name.isNotEmpty) { print(name); }
(5)List
・整数のインデックスが付けられたオブジェクトの集まり。Dartでは、配列はListオブジェクトとして定義する。
・dart:collection内に含まれるIterableインタフェースを実装し、for-inループを使用できる。
1)生成
var member = ['Tom', 'Bob']; List member = ['Tom', 'Bob']; List member = [ {'name': 'Tom', 'age': 22}, {'name': 'Bob', 'age': 29}, ]; List member = new List(); List<int> numbers_list = [1, 2, 3, 4, 5];
2)追加
①末尾に追加
my_list.add(6);
my_list.addAll([7, 8, 9]);
②インデックスを指定して追加
my_list.insert(0, 9);
my_list.insertAll(5, [3, 3, 3]);
③インデックスを指定して置換
my_list.setAll(3, [5, 5, 5]);
④インデックスを指定して削除
my_list.remove(6);
my_list.removeRange(1, 2);
3)要素を表示
①すべての要素
member.forEach((a) => print(a['name'])); for(var my_element in my_list) { print(my_element); }
②インデックスを指定
print(my_list.indexOf(7));
print(my_list.elementAt(1));
print(my_list.sublist(1, 2));
print(my_list.getRange(0, 2));
③条件を指定
my_list.retainWhere((e) => e!=3);
print(my_list);
my_list.contains(‘two’));
4)その他の主なメソッド
length、isEmpty、isNotEmpty
(6)Set
・Listと類似しているが重複した要素を含むことが出来ない。
・dart:collection内に含まれるIterableインタフェースを実装し、for-inループを使用できる。
(7)Map
・キーと値で構成されるオブジェクト。
・キーと値はどの型のオブジェクトでも良い。
1)mapリテラルを使って生成
var gifts = { // Keys Values 'first' : 'partridge', 'second': 'turtledoves', 'fifth' : 'golden rings' }; Map<int, List> positions = { 1: [1, 3, 4, 1, 7, 8], 2: [4, 13, 1, 2, 4, 5], 3: [7, 8, 9, 10, 2, 4] }
2)Mapコンストラクタを使って生成
var gifts = new Map();
gifts[‘first’] = ‘partridge’;
gifts[‘second’] = ‘turtledoves’;
gifts[‘fifth’] = ‘golden rings’;
var nobleGases = new Map();
nobleGases[2] = ‘helium’;
nobleGases[10] = ‘neon’;
nobleGases[18] = ‘argon’;
3)既存のmapに追加
var gifts = {‘first’: ‘partridge’};
gifts[‘fourth’] = ‘calling birds’;
gifts.putIfAbsent(‘fourth’, () => ‘calling birds’);
gifts.addAll({‘second’: ‘partridge’, ‘fifth’: ‘golden rings’});
4)mapから値を取得
var gifts = {'first': 'partridge'}; print(gifts['first']); gifts.forEach((k, v) { print('Key ${k} = ${v}'); } for(var my_element in my_map.values) { print(my_element); }
map内に存在しないキーでアクセスするとnullがリターン。
5)その他の主なメソッド
remove、keys、values、length、isEmpty、inNotEmpty、clear
(8)Symbol
・Symbolオブジェクトは、Dartプログラム内で定義されたオペレータまたは識別子。
識別子の前に#をつけてsymbolを使用する。
(1)関数の宣言
1)引数と戻り値に型を指定した例
void printNumber(num number) { print('The number is $number.'); }
2)型を指定しない例
printNumber(number) { // Omitting types is OK. print('The number is $number.'); }
3)1行関数
void printNumber(num number) => print(‘The number is $number.’);
※ここでいう1行は式(expression)の場合の事で、文(statement)ではない。
if文は対象外だが、 conditional (?:) は対象。
(2)オプションパラメータ
1)オプションパラメータの概要
・関数は、必須とオプションのパラメータを持つ。必須パラメータを最初に記述し、後にオプションのパラメータを記述。
・オプションのパラメータは位置的パラメータ(positional parameters)と指名パラメータ(named parameters)がある。
・オプションのパラメータはデフォルト値を持つことが出来る。デフォルト値はリテラルなどコンパイル定数。デフォルト値が指定されていない場合は、nullが割り当てられる。
2)指名パラメータ(named parameters)
①宣言
{param1, param2, …} のように記述して宣言する
例)デフォルト値を指定しない
enableFlags({bool bold, bool hidden}) {
// …
}
例)デフォルト値を指定
enableFlags({bool bold: false, bool hidden: false}) {
// …
}
②関数の呼び出し
例)
enableFlags(bold: true, hidden: false);
enableFlags(bold: true);
3)位置的パラメータ(positional parameters)
①宣言
角括弧 []で囲んで宣言する
例)デフォルト値を指定しない
String say(String from, String msg, [String device]) {
// …
}
例)デフォルト値を指定
String say(String from, String msg, [String device = 'carrier pigeon', String mood]) { // ... }
②関数の呼び出し
例)
say(‘Bob’, ‘Howdy’);
say(‘Bob’, ‘Howdy’, ‘smoke signal’);
(3)main()関数
・すべてのアプリはトップレベルmain()関数を持つ必要がある。
・main()関数はvoidをリターンし、オプションの引数としてList<String>パラメータを持つことが出来る。
例)
(宣言)
void main(List<String> arguments) { print(arguments); assert(arguments.length == 2); assert(int.parse(arguments[0]) == 1); assert(arguments[1] == 'test'); }
(コマンドラインで実行)
dart args.dart 1 test
(4)第一級オブジェクト(first-class object)
・他の関数にパラメータとして関数を渡す事が出来る。
例)
printElement(element) { print(element); }
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
・関数を変数に割り当てる事が出来る。
例)
var loudify = (msg) => ‘!!! ${msg.toUpperCase()} !!!’;
(5)構文スコープ(Lexical scope)
・Dartは、構文的にスコープづけされた言語で、変数のスコープは静的にコードのレイアウトによって決定される。
宣言されたブロック内でスコープ付けされていて、波括弧で括られたブロック{…}を見ればその変数のスコープを知ることができる。トップ・レベルの変数は何処からでも可視である。
(6)構文クロージャ(Lexical closures)
・クロージャはその関数がたとえオリジナルの構文スコープ外で使われても、その構文スコープ内の変数をアクセスしている関数オブジェクト。
例)
Function makeAdder(num addBy) { return (num i) => addBy + i;// 関数を返す関数定義 } main() { var add2 = makeAdder(2);// 2を加算する関数をadd2という変数として扱う var add4 = makeAdder(4); assert(add2(3) == 5); assert(add4(3) == 7); }
(7)関数のリターン
すべての関数は値をリターンする。指定されていない場合は、
return null;
が関数のボディに暗黙的に追加される。
これらのメソッドは特別で他のオブジェクトのように値を割り当てる事が出来ない。
・インスタンス変数に作用することが出来るがクラス変数に対してもアクセスできる。
・デフォルトで定義されている演算子をオーバーライド、再定義する事が出来る。
1)数値演算子
+,-,-expr,*,/,%
~/ 割った結果の整数部をリターン
●インクリメント
++var、var++、–var、var–
例)
a = 0;
b = ++a; // a:1、b:1
a = 0;
b = a++; // a:1、b:0
2)比較演算子
==,!=,>,<,>=,<= 3)型テスト演算子
as 型キャスト
is,is! 型テスト
4)代入演算子
= ?= /= %= >>= ^=
+= *= ~/= <<= &= |= ※ a op= b → a = a op b a += b a = a + b 5)論理演算子
!expr、||、&&
6)ビット演算子、シフト演算子
&,|,^,~expr,<<,>>
7)その他
() 関数のコール
[] List内の指定したインデックスにアクセス
expr1 ? expr2 : expr3
expr1がtrueの場合はexpr2を実行し、それ以外はexpr3を実行
. 式の属性にアクセス
.. カスケード。オブジェクトのメンバーに対して複数の操作を実行。
(1)if else文
if (isRaining()) { you.bringRainCoat(); } else if (isSnowing()) { you.wearJacket(); } else { car.putTopDown(); }
※Dartでは、JavaScriptと異なりtrue以外はすべてfalseとして扱う。
※簡単な制御構造の場合は、演算子を使って記述する事も出来る。
expr1 ? expr2 : expr3
(2)forループ
1)forループ
var message = new StringBuffer("Dart is fun"); for (var i = 0; i < 5; i++) { message.write('!'); }
●Forループ内のclosure
var callbacks = []; for (var i = 0; i < 2; i++) { callbacks.add(() => print(i)); } callbacks.forEach((c) => c());
Dartでは、上記コードもインデックスをキャプチャし、正常に実行できる。
JavaScriptでは、同様の下記コードで実行結果が2となってしまう。
var callbacks = []; for (var i = 0; i < 2; i++) { var fn = function() { console.log(i); } callbacks.push(fn); } callbacks.forEach(function(c) { c(); });
2)forEach
candidates.forEach((candidate) => candidate.interview());
3)for-in
Iterableクラス(list、Map、Set)はfor-inもサポート。
var collection = [0, 1, 2]; for (var x in collection) { print(x); }
(3)while do-while文
1)基本
while (!isDone()) { doSomething(); } do { printLine(); } while (!atEndOfPage());
2)break continue
・breakを使ってループの途中で終了できる。
while (true) { if (shutDownRequested()) break; processIncomingRequests(); }
・continueを使ってループ内の残りの処理をスキップして次のループに進むことが出来る。
for (int i = 0; i < candidates.length; i++) { var candidate = candidates[i]; if (candidate.yearsExperience < 5) { continue; } candidate.interview(); }
Iterableクラス(list、Map、Set)は、上記を下記のように記述する事も出来る。
candidates.where((c) => c.yearsExperience >= 5) .forEach((c) => c.interview());
(4)switch case文
integer、string、コンパイル定数を==を使って比較して使用。
1)基本
var command = 'OPEN'; switch (command) { case 'CLOSED': executeClosed(); break; case 'PENDING': executePending(); break; case 'APPROVED': executeApproved(); break; case 'DENIED': executeDenied(); break; case 'OPEN': executeOpen(); break; default: executeUnknown(); }
2)空のcase文
下記のように空のcase文もサポート。後続の処理が継続。
var command = 'CLOSED'; switch (command) { case 'CLOSED': // Empty case falls through. case 'NOW_CLOSED': // Runs for both CLOSED and NOW_CLOSED. executeNowClosed(); break; }
ラベルを使って書き換えた例
var command = 'CLOSED'; switch (command) { case 'CLOSED': executeClosed(); continue nowClosed; // Continues executing at the nowClosed label. nowClosed: case 'NOW_CLOSED': // Runs for both CLOSED and NOW_CLOSED. executeNowClosed(); break; }
(5)assert
assert文を使って指定した条件を評価し、falseの場合に例外を発生して中断できる。
assert(text != null);
assert(number < 100); assert文はchecedモードでのみ動作し、productionモードでは無視される。
・Dartでは、例外のスローとキャッチが出来る。
・Javaとは対照的に、Dartのすべての例外はチェックされない例外。
メソッドはどの例外をスローするか定義せず、例外をキャッチしなくてもよい。
・Dartは、ExceptionとErrorの型を用意しているが、ExceptionやErrorの型以外の非nullオブジェクトを例外としてスローできる。
2)スロー
例1)Exception型を使った例
throw new FormatException('Expected at least 1 section');
例2)任意のオブジェクトをスロー
throw 'Out of llamas!';
3)キャッチ
・例外の捕捉によって、例外の伝播を止める。
・onやcatchキーワードを使用する。
・onは、例外の型を指定する際に使用する。
・catchは、例外ハンドラが例外オブジェクトを必要とする際に使用する。
・複数の例外の型をスローする場合、複数のcatch文を指定する。
例1) try { breedMoreLlamas(); } on OutOfLlamasException { buyMoreLlamas(); } 例2) try { breedMoreLlamas(); } on OutOfLlamasException { // A specific exception buyMoreLlamas(); } on Exception catch (e) { // Anything else that is an exception print('Unknown exception: $e'); } catch (e) { // No specified type, handles all print('Something really unknown: $e'); }
4)finally
例外の発生に関わらずあるコードを実行させたい場合は、finally文を使用する。
例外が捕捉されなかった場合、例外はfinally文が実行された後で伝播される。
例1) try { breedMoreLlamas(); } finally { // Always clean up, even if an exception is thrown. cleanLlamaStalls(); } 例2) try { breedMoreLlamas(); } catch(e) { print('Error: $e'); // Handle the exception first. } finally { cleanLlamaStalls(); // Then clean up. }
(1)クラスの概要
・Dartは、クラスとミックスインベースの継承を使ったオブジェクト指向言語。
・ミックスインベースの継承とは、オブジェクトを除くすべてのクラスは一つのスーパークラスを持ち、クラスのボディは複数のクラス階層を再利用できる構造。
(2)オブジェクト
1)オブジェクトの生成
・クラスのコンストラクタにnewキーワードを指定。
・コンストラクタ名は、クラス名またはクラス名.識別子。
例)
var p1 = new Point(2, 2);
var p2 = new Point.fromJson(jsonData);
2)オブジェクトのインスタンス変数、メソッド
・インスタンス変数、メソッドにアクセスする場合はドットを使用する。
例)
var p = new Point(2, 2);
p.y = 3;
num distance = p.distanceTo(new Point(4, 4));
3)カスケードオペレータ
一つのオブジェクトのメンバーに対し続けて操作する場合は、カスケードオペレータ(..)を使用する事が出来る。
例)
querySelector('#button') // Get an object. ..text = 'Confirm' // Use its members. ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
4)コンスタントコンストラクタ
・コンスタントコンストラクタを使ってコンパイル時の定数を作成出来る。
この場合は、newキーワードの代わりにconstキーワードを使用する。
例)
var p = const ImmutablePoint(2, 2);
class Point { num x; num y; num z = 0; }
・宣言して値が割り当てられていない変数にはnullが割り当てられている。
●暗黙的なgetter、setterメソッド
・すべてのインスタンス変数は暗黙的にgetterメソッドが生成される。
・非finalのインスタンス変数は、暗黙的にsetterメソッドが生成される。
・コンストラクタやメソッドではなく、宣言した場所でインスタンス変数を初期化した場合、インスタンス変数が生成される時(コンストラクタとそのイニシャライザリストが実行される前)に値がセットされる。
例)
class Point { num x; num y; } main() { var point = new Point(); point.x = 4; // xの暗黙的setterメソッド使用 assert(point.x == 4); // xの暗黙的getterメソッド使用 }
・クラスの名前と同じ名前の関数を定義してコンストラクタを宣言。
class Point { num x; num y; Point(num x, num y) { this.x = x; this.y = y; } }
下記のように簡潔に記述する事も出来る。
Point(this.x, this.y);
●デフォルトコンストラクタ
・コンストラクタを定義されていない場合、デフォルトコンストラクタが提供される。
・デフォルトコンストラクタは引数を持たず、スーパークラスの非引数コンストラクタを呼び出す。
●コンストラクタの継承
・サブクラスは、スーパークラスからコンストラクタを継承しない。コンストラクタを定義していないサブクラスは、デフォルトコンストラクタのみを持つことになる。
●名前付きコンストラクタ
・クラス名のあとにドット'.'と識別子を付け、名前付きコンストラクタを定義し、複数のコンストラクタを持たせることができる
例)
class Point { num x; num y; Point(this.x, this.y); Point.fromJson(Map json) { x = json['x']; y = json['y']; } }
●スーパークラスのコンストラクタを明示的に呼び出す
下記例のようにコンストラクタのボディの前にコロンとスーパークラスのコンストラクタを記述する。
例)
class Person { Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } }
●イニシャライザリスト
・コンストラクタのボディが実行される前にインスタンス変数を初期化できる。
・イニシャライザ・リストはコロン':'で始まり、カンマで区切られた個々のイニシャライザ(初期化子ともいう)のリストで構成される。
例)
class Point { num x; num y; Point(this.x, this.y); Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)'); } }
●リダイレクトコンストラクタ
・同じクラス内の他のコンストラクタにリダイレクト
例)
class Point { num x; num y; Point(this.x, this.y); Point.alongXAxis(num x) : this(x, 0); }
●常数コンストラクタ
・constキーワードを使用。
・すべのインスタンス変数はfinalになる。
例)
class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); }
●ファクトリコンストラクタ
・factoryキーワードを使用。
・常にそのクラスの新しいインスタンスを生成するわけでは無い場合に使用。例えば、キャッシュからインスタンスを返したり、サブタイプのインスタンスを返す場合など。
例)
class Logger { final String name; bool mute = false; static final Map_cache = {}; factory Logger(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { final logger = new Logger._internal(name); _cache[name] = logger; return logger; } } Logger._internal(this.name);
・オブジェクトのインスタンスメソッドは、インスタンス変数とthisにアクセスできる。
例)
import 'dart:math'; class Point { num x; num y; Point(this.x, this.y); num distanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy); } }
●ゲッターとセッター
・ゲッターとセッターは、オブジェクトの属性に対するread、writeアクセスを提供する特別なメソッド。
・それぞれのインスタンス変数は、暗黙的なゲッター、セッターを持つ。
・ゲッターとセッターを使って(get、setキーワードを使用)、追加で属性を定義出来る。
例)
class Rectangle { num left; num top; num width; num height; Rectangle(this.left, this.top, this.width, this.height); num get right => left + width; set right(num value) => left = value - width; num get bottom => top + height; set bottom(num value) => top = value - height; } main() { var rect = new Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
●抽象メソッド(abstractメソッド)
・インスタンス、ゲッター、セッターメソッドをabstractにする事が出来る。
抽象メソッドをインタフェースのように定義し、実装は他のクラスで行うといったことが出来る。
・ボディ部がないメソッドが抽象メソッドとして扱われる。abstractキーワードは使用しない。
例)
abstract class Doer { // インスタンス変数やメソッドを定義 void doSomething(); // Define an abstract method. } class EffectiveDoer extends Doer { void doSomething() { // 実際の処理内容を記述 } }
・抽象クラスを定義する場合は、abstractを指定する。
・abstractクラスはインスタンス化することができないクラス。
・abstractクラスは、インタフェースを定義するのに有用。
・インスタンス化出来るようにするにはファクトリ・コンストラクタが必要
・abstractクラスは、abstractメソッドを持つ場合が多い。
例) abstract class AbstractContainer { // ...Define constructors, fields, methods... void updateChildren(); // Abstract method. }
例)abstractクラスを使用する例 class SpecializedContainer extends AbstractContainer { // ...Define more constructors, fields, methods... void updateChildren() { // ...Implement updateChildren()... } // Abstract method causes a warning but // doesn't prevent instantiation. void doSomething(); }
●暗黙的なインターフェイス(implicit interface)
・すべてのクラスは、そのクラスと実装するインタフェースのすべてのインスタンスメンバーを含むインタフェースを暗黙的に定義している。
・あるクラスBを継承せず、BのAPIをサポートするクラスAを作成したい場合、クラスAはBのインタフェースを実装する必要がある。
・一つのクラスは、implementsを使って宣言することによって複数のインタフェースを実装できる。
例) class Person { final _name; Person(this._name); String greet(who) => 'Hello, $who. I am $_name.'; } //Personインタフェースを実装 class Imposter implements Person { final _name = ""; String greet(who) => 'Hi $who. Do you know who I am?'; } greetBob(Person person) => person.greet('bob'); main() { print(greetBob(new Person('kathy'))); print(greetBob(new Imposter())); }
例)複数のインタフェースを実装
class Point implements Comparable, Location {
// ...
}
"extends"を使ってサブクラスを作成し、スーパークラスを参照する場合は"super"を指定する。
例) class Television { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ... } class SmartTelevision extends Television { void turnOn() { super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ... }
●オーバーライド
・サブクラスは、インスタンスメソッド、ゲッター、セッターをオーバーライドできる。
例)ObjectクラスのnoSuchMethod()をオーバーライドする例 class A { void noSuchMethod(Invocation mirror) { print('You tried to use a non-existent member:' + '${mirror.memberName}'); } }
●@overrideアノテーション
あるメンバをオーバーライドしていることを明示的に示すのに@overrideアノテーションを指定する事が出来る。
例) class A { @override void noSuchMethod(Invocation mirror) { // ... } }
●@proxyアノテーション
上記例で、noSuchMethod()をすべてのゲッター、セッター、メソッドに対して使用する場合は、@proxyアノテーションを指定すると警告を回避できる。
@proxy class A { void noSuchMethod(Invocation mirror) { // ... } }
・ある一定数の数の定数を表すのに使用される特別なクラス。
・バージョン1.8で導入された新しい機能。
・Dartエディタでenumを使用するには、"Preferences"→"Experimental"→"Enable Enum Support"で有効にする必要がある。
・dartanalyzerやdart2jsで使用する場合には、下記のようにオプションを指定する。
dartanalyzer --enable-enum enum_switch.dart
dart2js --enable-enum main.dart
●enumの宣言
例) enum Color { red, green, blue }
●indexゲッター
enumで定義した各値は、0から始まるindexゲッターを持つ。
例)
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
●enum内のすべての値を取得
valuesを使って取得
例)
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
●switch文
例) enum Color { red, green, blue } // ... Color aColor = Color.blue; switch (aColor) { case Color.red: print('Red as roses!'); break; case Color.green: print('Green as grass!'); break; default: // Without this, you see a WARNING. print(aColor); // 'Color.blue' }
●enumの制限
・サブクラス、ミックスイン、enumの実装は出来ない。
・enumを明示的にインスタンス化できない。
・ミクスインを使用するには、付加するクラスの前にwithキーワードを指定する。
・ミクスインを実装するには、オブジェクトを継承するクラスを作成する。コンストラクタは宣言せず、superもコールしない。
例) class Musician extends Performer with Musical { // ... } class Maestro extends Person with Musical, Aggressive, Demented { Maestro(String maestroName) { name = maestroName; canConduct = true; } } abstract class Musical { bool canPlayPiano = false; bool canCompose = false; bool canConduct = false; void entertainMe() { if (canPlayPiano) { print('Playing piano'); } else if (canConduct) { print('Waving hands'); } else { print('Humming to self'); } } }
●クラス変数
例) class Color { static const red = const Color('red'); final String name; // インスタンス変数 const Color(this.name); // A constant constructor. } main() { assert(Color.red.name == 'red'); }
※クラス変数は使用されるまで初期化されない。
●クラスメソッド
・クラスメソッドはインスタンス上では実行されない。thisキーワードを使ってアクセスする事も出来ない。
例) import 'dart:math'; class Point { num x; num y; Point(this.x, this.y); static num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } main() { var a = new Point(2, 2); var b = new Point(4, 4); var distance = Point.distanceBetween(a, b); assert(distance < 2.9 && distance > 2.8); }
※共通して広範囲に使用されるユーティリティなどはクラスメソッドの代わりにトップレベル関数を使用する事を検討する。
※クラスメソッドをコンパイル時定数として使用する事が出来る。例えば、constantコンストラクタの引数としてクラスメソッドを渡す事が出来る。
・List<E>などと宣言している場合に総称型を利用している。
・総称型のオブジェクトはそれらの型引数の情報を実行時に保持する。
・例えば文字列のリストを宣言する場合は、List<String>と記述する。
●型付けは必須ではない
(String型でリストを使用する場合) new List<String>(); (指定しない場合) new List();
この場合Dartは、下記のようにdynamic(未知:unknow)という意味の型が指定されたと解釈する。
new List<dynamic>();
●間違ってリスト内に数値を追加した場合にミスである事をチェックできる
例)
var names = new List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
// ...
names.add(42); // Fails in checked mode (succeeds in production mode).
●コードの重複を軽減できる
(総称型を使用しない場合) abstract class ObjectCache { Object getByKey(String key); setByKey(String key, Object value); } abstract class StringCache { String getByKey(String key); setByKey(String key, String value); } (総称型を使用する場合) abstract class Cache<T> { T getByKey(String key); setByKey(String key, T value); }
●Listとmapでの使用例
var names = <String>['Seth', 'Kathy', 'Lars']; var pages = <String, String>{ 'index.html': 'Homepage', 'robots.txt': 'Hints for web robots', 'humans.txt': 'We are people, not machines' };
●型テストの為の演算子であるisが使える
コンストラクタ内では、型パラメタは実行時に必要であり、それらは実行時に渡されるので、型テストの為の演算子であるisが使える
new List<String>() is List<Object> // true: 各文字列はオブジェクトである
new List<Object>() is List<String> // false: 総てのオブジェクトが文字列ではない
・すべてのDartアプリはライブラリ。
・アンダースコアで始まる(_)識別子はライブラリ内でのみアクセスできる。
・ライブラリはパッケージを使って配布できる。
●ライブラリを使用する方法
・importを指定してライブラリを別のライブラリのスコープ内で使うよう指定する。ビルトインのライブラリは、"dart:"をURIに指定。
例)
import 'dart:html';
・ビルトイン以外のライブラリは"package:"を指定してファイルのパスを指定。パッケージ・マネージャが用意したライブラリであることを意味する。
例)
import 'package:mylib/mylib.dart';
●ライブラリにプレフィックスを指定
同じ識別子を持つ二つのライブラリをインポートする場合、ライブラリにプレフィックスを指定して区別する事ができる。
例)
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
var element1 = new Element(); //lib1
var element2 = new lib2.Element(); //lib2
●ライブラリ内の一部分をインポート
例)
//fooのみインポート
import 'package:lib1/lib1.dart' show foo;
//foo以外インポート
import 'package:lib2/lib2.dart' hide foo;
●Lazily loading、Deferred loading
・アプリ内でそのライブラリを使用する段階になってからロードするように設定
・"deferred as"を指定してインポートし、ライブラリを使用する場所でloadLibrary()を呼び出す。
・loadLibrary()メソッドは非同期なので、ライブラリロード後に実行する関数をthenメソッドで指定する。
・ライブラリのロードは必要になってからの一回だけで、loadLibrary()メソッドはその後何度も呼び出す事が事が出来る。
例) import 'package:deferred/hello.dart' deferred as hello; hello.loadLibrary().then((_) { hello.printGreeting(); });
●ライブラリの宣言
・"library"を使ってライブラリの名前を指定。
・"part"を使ってライブラリ内に追加で組み入れるファイルを指定。
ライブラリ宣言をしたファイル内に"part fileUri"を使って追加するファイルのパスを指定し、追加するファイル内には"part of identifier"を使ってインポート先のライブラリ名を指定する。
例) library ballgame; import 'dart:html'; // ...Other imports go here... part 'ball.dart'; part 'util.dart'; (bail.dart) part of ballgame; // ...Code goes here... (util.dart) part of ballgame; // ...Code goes here...
・async関数とawaitが利用できる。
・Dartライブラリは、Futureオブジェクトをリターンする関数をサポートしている。
これらの関数は非同期で、I/O処理など時間がかかる処理をセッティング後、すぐにリターンさせる事が出来る。
・上記非同期関数の結果を使用する必要がある場合、以下二つのいずれかの選択肢がある。
①asyncとawaitを使用。
②Future APIを使用。
●asyncとawaitの使用例
awaitを使用する場合、関数にasyncを指定しなければならない。
checkVersion() async { var version = await lookUpVersion(); if (version == expectedVersion) { // Do something. } else { // Do something else. } }
●v1.8で非同期サポートを使用する方法
Dartエディタ、dartanalyzer、Dart VM(dart)で非同期サポートを使用する場合、設定を有効にする必要がある。
"Preferences"→"Experimental"→"Enable Async Support"
※v1.8のDart-to-JSコンパイラ(dart2js)は、非同期をサポートしていない。下記対応が必要。
https://github.com/dart-lang/async_await#async-await-transformer
●asyncで関数を宣言
例)同期処理から非同期に定義しなおす
String lookUpVersionSync() => '1.0.0';
↓
Future<String> lookUpVersion() async => '1.0.0';
※関数内でFuture APIを使用する必要はない。Dartが必要に応じてFutureオブジェクトを生成する。
例)main関数内全体で利用する場合 main() async { checkVersion(); print('In main: version is ${await lookUpVersion()}'); }
●awaitを使用
書式
await expression
・"expression"の値は、Future型でなければならず、オブジェクトをリターンするpromiseである。
"await expression"の値は、このリターンされたオブジェクト。
・関数を変数や戻り値の型として使うときにはtypedefプレフィックスを使った関数型エイリアスを使用する。
関数型を変数に割り当てる際、typedefによって変数や戻り値の型の情報を維持できる。
1)関数を変数に割り当てる例
①typedefを使用しない場合
下記コードでは、mainの2行目で数値と文字列で型が一致していないが、関数を変数に割り当てた際に関数の型の情報が失われてしまっていて、Dartエディタで警告が表示されない。
String logger(String msg) { var today = new DateTime.now(); return '${today.toLocal()}-${msg}'; } void main() { var log1 = logger; var addLog1 = 1 + log1('My test message1'); print(addLog1); }
②typedefを使用した場合
下記コードでは、typedefを使用しているので型の情報が維持されて、Dartエディタで警告が発生する。
String logger(String msg) { var today = new DateTime.now(); return '${today.toLocal()}-${msg}'; } typedef String AliLog(String msg); void main() { AliLog log2 = logger; var addLog2 = 2 + log2('My test message2'); print(addLog2); }
2)関数を他の関数の引数として渡す例
String logger(String msg) { var today = new DateTime.now(); return '${today.toLocal()}-${msg}'; } typedef String AliLog(String msg); num logLength(String msg,log) { return log(msg).length; } void main() { var length = logLength('My test message',logger); print(length); }
3)任意の関数の型をチェック
・typedefは単に別名であるので、任意の関数の型をチェックする事も出来る。
例) typedef int Compare(int a, int b); int sort(int a, int b) => a - b; main() { assert(sort is Compare); // True! }
・メタデータのアノテーションは、文字@で始まり以下コンパイル時定数への参照、または定数コンストラクタ呼び出しが続く。
・@deprecated、@override、@proxyの3つのアノテーションが利用できる。
@override、@proxyはクラスの拡張などで利用される。
@deprecatedは、クラス、ゲッタ、セッタ、メソッド、トップレベル変数、あるいはトップレベル関数に対し、もはや使えなくなっているものだということをマークする為に使用する。
・メタデータはライブラリ、クラス、typedef、型パラメタ、コンストラクタ、ファクトリ、関数、パラメタ、変数宣言、インポートまたはエクスポートディレクティブの前に置く事が出来、リフレクションを使って実行時にメタデータを取得できる。
・Dartアプリはパッケージに依存。
・一つまたはそれ以上のライブラリパッケージを使用する場合は、そのアプリ自身もアプリケーションパッケージでなければならない。
・Dartパッケージの管理にpubツールを使用できる。
●Dartのパッケージ
・pubspecファイルを含むディレクトリがDartパッケージ。
・pubspecファイルには、パッケージに関するメタデータが含まれる。
・pubspecファイルには、依存するパッケージを記述する事が出来る。
・Dartパッケージ内でライブラリを使用するには、下記が必要。
①pubspecファイルを作成
②pubを使って依存するパッケージを取得
③ライブラリをインポート
1)pubspec.yamlを作成
・アプリケーションパッケージのディレクトリの最上位の階層に置く。
・最低限、パッケージ名を記述する。
・必要に応じて依存するパッケージを記述。
例)
name: my_app
dependencies:
js: any
intl: any
2)パッケージをインストール
・アプリケーションパッケージのルートディレクトリでpub getを実行する
・pub getを実行するとpubspecファイルに記述されているパッケージとの依存性を解決し、依存するその他のパッケージも含めてダウンロードし、キャッシュ内に置く。
・ルートディレクトリ内にpackagesディレクトリを作成し、そこに依存するパッケージへのリンクを置く。
・最初に依存するパッケージをダウンロードする際は、pubspecファイルの指定を満たす最新のバージョンがダウンロードされ、そのダウンロードしたパッケージ名とそのバージョンのリストがpubspec.lockファイルに記述される。
明示的にアップグレードを実行しなければしなければ、該当パッケージの最新バージョンがリリースされてもこのロックファイルに記述されているバージョンが維持される。
1)pubspec.yamlに依存性を記述
dependencies:
sqljocky: ">=0.11.0 <0.12.0" 2)pubツールでインストール
$ pub get
3)Dartのコード内でインポート
import 'package:sqljocky/sqljocky.dart';
4)サンプルコード
Select文でデータを取得し、表示してみました。
import 'package:sqljocky/sqljocky.dart'; void main () { var pool = new ConnectionPool( host: 'localhost', user: 'dbuser', password: 'xxxxx', db: 'db_name', max: 5 ); pool.query("select id,total from sample").then((results) { results.forEach((row) { print('id: ${row[0]}, total: ${row[1]}'); }); }); }