📃
Dart ドキュメント読み直した備忘録
Dart2.18.5で検証
Objectとdynamic
Objectは全てのオブジェクトを許容する。dynamicは全てのオブジェクトと全ての操作を許容する。
Object obj = "foo";
obj.foo(); // ビルドできない
dynamic obj = "foo";
obj.foo(); // ビルドできるが実行時にエラー
late
nullableでないインスタンス変数やトップレベル変数を後から初期化するときに使う。lateで宣言した変数は初めて使用されるときに初期化される。
これはビルドできない
String foo;
class Bar {
String baz;
}
void main() {
・・・
}
lateをつける
late String foo;
class Bar {
late String baz;
}
void main() {
・・・
}
また、トップレベル変数とクラス変数も初めて使用されるときに初期化される
var count = 0;
var global = (() => ++count)();
class Person {
static int param = (() => ++count)();
}
void main() {
var local = (() => ++count)(); // 1
global; // 2
Person.param; // 3
}
スプレット演算子
nullableな変数にも使える
List<int>? list;
・・・
[...?list];
List、Map、Set
List
var foo = <int>[1, 2, 3]
foo; // [1, 2, 3]
MapとSet
var foo = Map<String, int>();
foo["id"] = 1;
foo; // {id: 1}
var foo = Set();
foo.addAll([1, 2, 3]);
foo; // {1, 2, 3}
これと
var foo = <String, int>{"id": 1};
foo; // {id: 1}
var foo = <int>{1,2,3};
foo; // {1, 2, 3}
は同義である。また、それぞれifやforが使える
var list = [
for (var i = 0; i < 3; i++) i,
if (condition) "foo",
];
list; // [0, 1, 2, foo]
var set = {
for (var i = 0; i < 3; i++) i,
if (condition) "foo",
};
set; // {0, 1, 2, foo}
var map = {
for (var i = 0; i < 3; i++) "$i": i,
if (condition) "foo": "bar",
};
map; // {0: 0, 1: 1, 2: 2, foo: bar}
Function
Functionもオブジェクト
final String Function(int n) f = (int n) {
return "n is $n";
};
f(1); // n is 1
戻り値を指定しないかつreturnがないとnullが返る
foo () {}
foo(); // null
オブジェクトのカスケード記法
var foo = Foo();
hoge.bar = "bar";
hoge.baz();
と
Foo()
..bar = "bar"
..baz();
は同義である
rethrow
これだとfooで例外が発生したところまでしか追えない
foo() {
try {
baz();
} catch (e) {
throw e;
}
}
try {
foo();
} catch (e, st) {
print(st);
}
bazで例外が発生したところまで追える
foo() {
try {
baz();
} catch (e) {
rethrow;
}
}
・・・
const
同じ内容のオブジェクトをconstで定義すると1つのインスタンスになる
class Foo {
final String name;
const Foo({required this.name});
}
const foo = Foo(name: "foo");
identical(foo, Foo(name: "foo")); // false
identical(foo, const Foo(name: "foo")); // true
identical(foo, const Foo(name: "fooooo")); // false
コンストラクタ
コンストラクタは継承されない
class Foo {
String name;
Foo(this.name);
Foo.fromName(String v) : name = v;
factory Foo.withName(String value) => Foo(value);
}
class Bar extends Foo {}
// 継承されないのでビルドエラー
Bar("bar");
Bar.fromName("bar");
Bar.withName("bar");
ただし、名前なし・引数なしコンストラクタは継承される
class Foo {
String name;
Foo() : name = "foo";
}
class Bar extends Foo {}
Bar();
演算子の継承、追加
operatorを使う
class Foo {
String name;
Foo(this.name);
bool operator ==(Object other) => other is Foo && name == other.name;
bool operator <(Object other) => true;
int get hashCode => name.hashCode;
}
Foo("foo") == Foo("foo") // true
Foo("foo") < Foo("foo") // true
noSuchMethod
オーバーライドできる
class Foo {
void noSuchMethod(Invocation invocation) {
print("${invocation.memberName}?そんなめそっどないよー");
}
}
dynamic foo = Foo();
foo.foo();
extension
extensionで拡張できる
extension Foo on int {
void greet () => print("I am $this");
}
10.greet(); // I am 10
enumも拡張できる
enum Status { foo, bar }
extension StatusExt on Status {
String get name {
switch (this) {
case Status.foo:
return "foo";
case Status.bar:
return "bar";
}
}
}
Status.foo.name // foo
ジェネリクスに制約をつける
extendsを使う
foo<T extends num>(T n) {}
foo(100);
foo("test"); // num型じゃないのでビルドエラー
null許容変数から非許容変数への昇格
例
void main() {
List? bar;
・・・
if (bar != null){
bar.length; // !マークいらない
}
}
ただし、ローカル変数とパラメータに対してのみ有効なため、フィールドやトップレベル変数では昇格しない
class Foo {
List? bar;
baz() {
if (bar != null) {
bar!.length; // !マークがいる
}
}
}
ローカル変数にいれなおすとこの機能が有効になる
class Foo {
List? bar;
baz() {
var bar = this.bar;
if (bar != null) {
print(bar.length); // !マークいらない
}
}
}
Never型
関数やメソッドが正常に終了しないことを明示的に指定するための型
これだと正常に終了してしまうのでコンパイルエラー
Never foo() {}
必ず例外を投げる関数などを定義するときに使用する
Never foo() {
throw Exception();
}
その他
参考にしたドキュメントの範囲
Discussion