Contents
Effective Dart: Usage
You can use these guidelines every day in the bodies of your Dart code. Users of your library may not be able to tell that you’ve internalized the ideas here, but maintainers of it sure will.
これらのガイドラインは、あなたのDartコードの本文で毎日使用することができます。あなたのライブラリの利用者は、あなたがここに書かれていることを理解していると思わないかもしれませんが、メンテナンスする側には確実に伝わるはずです。
Libraries
These guidelines help you compose your program out of multiple files in a consistent, maintainable way.
これらのガイドラインは、一貫性のある保守性の高い方法で複数のファイルからプログラムを構成するのに役立ちます。
These guidelines help you compose your program out of multiple files in a consistent, maintainable way. To keep these guidelines brief, they use “import” to cover import
and export
directives. The guidelines apply equally to both.
このガイドラインを簡潔にするために、import ディレクティブと export ディレクティブをカバーするために、”import” を使用しています。このガイドラインは両者に等しく適用されます。
Many Dart developers avoid using part
entirely. They find it easier to reason about their code when each library is a single file.
多くの Dart 開発者は、パートを完全に使用することを避けています。
If you do choose to use part
to split part of a library out into another file, Dart requires the other file to in turn indicate which library it’s a part of.
各ライブラリが 1 つのファイルである方が、コードの推論がしやすいからです。
For legacy reasons, Dart allows this part of
directive to use the name of the library it’s a part of.
レガシーな理由から、Dartではこのpart of
ディレクティブに、そのライブラリの名前を使用することを許可しています。
That makes it harder for tools to physically find the main library file, and can make it ambiguous which library the part is actually part of.
そのため、ツールでメインのライブラリファイルを物理的に見つけることが難しくなり、その部分が実際にどのライブラリの一部であるかが曖昧になる可能性があります。
望ましい最新の構文は、他のディレクティブと同様に、ライブラリファイルを直接指す URI 文字列を使用することです。例えば、my_library.dart というライブラリがあったとします。
工事中🏗
DON’T import libraries that are inside the src
directory of another package.
別のパッケージのsrc
ディレクトリの中にあるライブラリをインポートしないでください。
The src
directory under lib
is specified to contain libraries private to the package’s own implementation.
libの中のsrcディレクトリは、パッケージ自体の実装にプライベートなライブラリを含むように指定されています。
The way package maintainers version their package takes this convention into account.
パッケージメンテナがパッケージをバージョン管理する方法では、この規則が考慮されます。
They are free to make sweeping changes to code under src
without it being a breaking change to the package.
それをそのパッケージのbreaking changeとして指定することなく、srcディレクトリ以下のコードに自由に抜本的な変更を加えることができます。
(プライベートなので、他者にインポートされない前提でメンテナンスされる、ということだと思われる)
That means that if you import some other package’s private library, a minor, theoretically non-breaking point release of that package could break your code.
つまり、他のパッケージのプライベートライブラリをインポートすると、そのパッケージのマイナーな、理論的にはブレークポイントのないリリースによってコードが破損する可能性があります。
DON’T allow an import path to reach into or out of lib
.
A package:
import lets you access a library inside a package’s lib
directory without having to worry about where the package is stored on your computer.
package:
でのインポートは、パッケージがコンピュータのどこに保存されているかを気にすることなく、パッケージのlibディレクトリ内のライブラリにアクセスできます。
Being discouraged is only the patter where all files of a package are tied together to a single library by having one library file and all other files being part of that library.
この機能を実現するためには、ディスク上の他のファイルに対して lib が相対的に存在するpathを必要とするようなインポートを行うことはできません。
In other words, a relative import path in a file inside lib
can’t reach out and access a file outside of the lib
directory, and a library outside of lib
can’t use a relative path to reach into the lib
directory.
工事中🏗
PREFER relative import paths.
Whenever the previous rule doesn’t come into play, follow this one.
前のルールが効かないときは、このルールに従います。
工事中🏗
Null
DON’T explicitly initialize variables to null
.
変数を明示的にnullで初期化しない。
If a variable has a non-nullable type, Dart reports a compile error if you try to use it before it has been definitely initialized.
変数が Non-Nullable型の場合、初期化される前に使用しようとする(その変数にアクセスしようとする)と、Dart はコンパイル エラーを報告します。
If the variable is nullable, then it is implicitly initialized to null
for you.
変数がnullable型の場合、自動的にnullで暗黙的に(自動的に)初期化されます。
There’s no concept of “uninitialized memory” in Dart and no need to explicitly initialize a variable to null
to be “safe”.
Dartには「初期化されていないメモリ」の概念はなく、「安全」のために変数を明示的にnullで初期化する必要はありません。
good↓
Item? bestDeal(List<Item> cart) {
Item? bestItem;
for (final item in cart) {
if (bestItem == null || item.price < bestItem.price) {
bestItem = item;
}
}
return bestItem;
}
cartの中の一番安いitemを返すメソッド。
bad↓
Item? bestDeal(List<Item> cart) {
Item? bestItem = null;//←nullで初期化しなくて良い
for (final item in cart) {
if (bestItem == null || item.price < bestItem.price) {
bestItem = item;
}
}
return bestItem;
}
DON’T use an explicit default value of null
.
If you make a nullable parameter optional but don’t give it a default value, the language implicitly uses null
as the default, so there’s no need to write it.
下記のサンプルのように、nullableな引数をオプショナルにして、デフォルト値を設定しない場合、Dart言語が暗黙的(自動的)にデフォルト値としてnullを使います。なので明示的にコードを書く必要はありません。
good↓
void error([String? message]) {
stderr.write(message ?? '\n');
}
bad↓
void error([String? message = null]) {
stderr.write(message ?? '\n');
}
PREFER using ??
to convert null
to a boolean value.
nullをbool値に変換する場合 ?? を使ってください。
This rule applies when an expression can evaluate to true
, false
, or null
, and you need to pass the result to something that expects a non-nullable boolean value.
このルールは、ある式がtrue,false,nullのいずれかの値をとる可能性があり、その結果をnon-nullableなbool値を期待するものにセットする必要があるケースに適用されます。
A common case is using the result of a null-aware method call as a condition.
一般的な具体例としては、null-aware演算子の結果が条件として使われる場合です。
good↓
// If you want null to be false:
// 左辺がnullの時は条件をfalseと評価したい
if (optionalThing?.isEnabled ?? false) {
print('Have enabled thing.');
}
// If you want null to be true:
// 左辺がnullの時は条件をtrueと評価したい
if (optionalThing?.isEnabled ?? true) {
print('Have enabled thing or nothing.');
}
bad↓
// If you want null to be false:
if (optionalThing?.isEnabled == true) {
print('Have enabled thing.');
}
// If you want null to be true:
if (optionalThing?.isEnabled != false) {
print('Have enabled thing or nothing.');
}
Both operations produce the same result and do the right thing, but ??
is preferred for three main reasons:
goodサンプルでもbadサンプルでも実行結果は同じですが、??
を使うのが好ましい理由が三つあります。
- The
??
operator signals that the code has something to do withnull
.
??
があることで、その箇所が「null値が関係する可能性がある箇所」であることがわかる。
- The
== true
looks like a common mistake where the equality operator is redundant and can be removed. That’s true when the boolean expression on the left will not producenull
, but not when it can.
== true
は、「==演算子は不要なので削除できる」という勘違いを招きやすい。==演算子の左辺がnull値になる可能性がない場合は削除できるが、null値になる可能性がある場合は削除してはいけない。
- The
?? false
and?? true
clearly show what value will be used when the expression isnull
. With== true
, you have to think through the boolean logic to realize that means that anull
gets converted to false.
?? false
と?? true
は、式がnullになる場合何が使われるのかが明確でわかりやすい。「==true」の場合、ブール論理を検討して、nullがfalseに変換されることを理解する必要がある。
Exception: Using a null-aware operator on a variable inside a condition doesn’t promote the variable to a non-nullable type. If you want the variable to be promoted inside the body of the if
statement, it might be better to use an explicit != null
check instead of ?.
followed by ??
:
条件式の中でnull-aware演算子を変数に対して使用しても、その変数が
good↓
if文のブロック内ではmessageはnon-nullable型としてpromoteされる(アナライザがnon-nullable型として認識する)、ということだと思われる。(if文の条件式でnullでないことを確認しているから)
int measureMessage(String? message) {
if (message != null && message.isNotEmpty) {
// message is promoted to String.
return message.length;
}
return 0;
}
bad↓
if文のブロック内でもmessageはnullable型なので、lengthフィールドを参照する場合、下記のように(Swiftの)強制アンラップをする必要がありますね、という話。
int measureMessage(String? message) {
if (message?.isNotEmpty ?? false) {
// message is not promoted to String.
return message!.length;
}
return 0;
}
AVOID late
variables if you need to check whether they are initialized.
初期化したかを確認する必要がある変数はlate変数にしない。
Dart offers no way to tell if a late
variable has been initialized or assigned to.
Dartは、遅延変数が初期化されたか、割り当てられたかを判断する方法を提供していません。
If you access it, it either immediately runs the initializer (if it has one) or throws an exception.
late変数にアクセスした場合、初期化が行われるか(それ以前のコードで初期化が行われていたなら)、例外がスローされるかのいずれかです。
Sometimes you have some state that’s lazily initialized where late
might be a good fit, but you also need to be able to tell if the initialization has happened yet.
lateキーワードを使って状態変数を遅延初期化するコードをうまく書けた、と思うようなケースでは、初期化が行われていることをしっかり確認する必要があります。
Although you could detect initialization by storing the state in a late
variable and having a separate boolean field that tracks whether the variable has been set, that’s redundant because Dart internally maintains the initialized status of the late
variable.
状態をlate変数に格納し、変数がセット(初期化)されているかどうかを追跡する別のboolフィールドを設定することで初期化を検出できますが、Dartは後期変数の初期化ステータスを内部的に維持するため、これは冗長です。
Instead, it’s usually clearer to make the variable non-late
and nullable. Then you can see if the variable has been initialized by checking for null
.
代わりに変数をnullableかつnon-lateにする方が明快です。それで変数の値がnullかどうかを調べれば済みます。
Of course, if null
is a valid initialized value for the variable, then it probably does make sense to have a separate boolean field.
もちろん、nullが変数の有効な初期化値であれば、booleanフィールドを別に持つことは意味があるでしょう。
CONSIDER assigning a nullable field to a local variable to enable type promotion.
(nullable→non-nullableの)型の昇格を有効にするために、null許容(nullable)フィールドをローカル変数に割り当てることを検討してください。
Checking that a nullable variable is not equal to null
promotes the variable to a non-nullable type.
nullableな変数の値がnullでないことを確認することで、その変数をnon-nullable型へpromotionできます。
That lets you access members on the variable and pass it to functions expecting a non-nullable type.
promotionによってその変数のメンバーに(アンラップ無しで)アクセスできますし、あるいはnon-nullable型を引数として受け取る関数に(アンラップ無しで)渡すこともできるようになります。
Unfortunately, promotion is only sound for local variables and parameters, so fields and top-level variables aren’t promoted.
残念ながら、昇格はローカル変数とパラメータに対してのみ有効であるため、フィールドとトップレベル変数は昇格されません。
good↓
toString()メソッドの中でローカル変数tempResponseを宣言して、それに対してnullチェックをすることで、if文のブロック内ではtempResponseはpromotionされるので、url、errorCode、reasonなどのメンバーに(アンラップ無しで)アクセスできますね、という話。
class UploadException {
final Response? response;
UploadException([this.response]);
@override
String toString() {
final tempResponse = this.response;
if (tempResponse != null) {
return 'Could not complete upload to ${tempResponse.url} '
'(error code ${tempResponse.errorCode}): ${tempResponse.reason}.';
}
return 'Could not upload (no response).';
}
}
Assigning to a local variable can be cleaner and safer than using !
every place the field or top-level variable is used:
ローカル変数に代入することは、フィールドやトップレベル変数が使われているすべての箇所で!
を使って強制アンラップするよりもクリーンで安全です。
上記のように説明されている。まあ確かに「実行時エラーを出さない」という点では安全なのだが、結局重要なのは、コードを書く人がロジックの中で変数がnull値をとる可能性があるのか否かを「適切に把握して適切にハンドリングするコードを書くこと」なのだとは思う。
bad↓
class UploadException {
final Response? response;
UploadException([this.response]);
@override
String toString() {
if (response != null) {
return 'Could not complete upload to ${response!.url} '
'(error code ${response!.errorCode}): ${response!.reason}.';
}
return 'Could not upload (no response).';
}
}
Be careful when using a local variable. If you need to write back to the field, make sure that you don’t write back to the local variable instead.(Making the local variable final
can prevent such mistakes.) Also, if the field might change while the local is still in scope, then the local might have a stale value. Sometimes it’s best to simply use !
on the field.
ローカル変数を使うときは、注意が必要です。もし、フィールドに書き戻す必要がある場合、代わりにローカル変数に書き戻さないように注意してください(ローカル変数をfinalにすると、このような間違いを防ぐことができます)。また、ローカル変数がまだスコープ内にあるときにフィールドの値が変わった場合、ローカル変数にフィールドの値がセットされる可能性があります。
↑
すごくわかりにくい笑
確かにそういう可能性はありそう。結局「修正する際は間違えないように注意しましょう」ということに尽きる。
あとは自分でよくわからないようなコードをあえて書こうとしなければ、あまりこういうミスは発生しずらい気はするが。
Strings
Here are some best practices to keep in mind when composing strings in Dart.
Dartで文字列を扱う場合に覚えておくべきベストプラクティスです。
DO use adjacent strings to concatenate string literals.
文字列リテラルを連結するには、隣接する文字列を使用してください。
If you have two string literals—not values, but the actual quoted literal form—you do not need to use +
to concatenate them.
値ではなくリテラル形式の文字列リテラルが二つあってそれらを結合(つなげる)する時+
演算子を使う必要はありません。
Just like in C and C++, simply placing them next to each other does it. This is a good way to make a single long string that doesn’t fit on one line.
CやC++の場合と同じように、それらを並べて配置するだけで実行できます。 これは、1行に収まらない単一の長い文字列を作成するための良い方法です。
good↓
raiseAlarm('ERROR: Parts of the spaceship are on fire. Other '
'parts are overrun by martians. Unclear which are which.');
bad↓
raiseAlarm('ERROR: Parts of the spaceship are on fire. Other ' +
'parts are overrun by martians. Unclear which are which.');
PREFER using interpolation to compose strings and values.
補間を使用して文字列と値を組み合わせましょう。
If you’re coming from other languages, you’re used to using long chains of +
to build a string out of literals and other values. That does work in Dart, but it’s almost always cleaner and shorter to use interpolation:
他の言語から来た人は、リテラルや他の値から文字列を構築するために長い+の連鎖を使用することに慣れていると思います。Dartでもそれは可能ですが、ほとんどの場合、補間を使用する方がよりクリーンで短かく済むでしょう。
good↓
'Hello, $name! You are ${year - birth} years old.';
bad↓
'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';
Note that this guideline applies to combining multiple literals and values. It’s fine to use .toString()
when converting only a single object to a string.
このガイドラインは複数のリテラルや値をつなげる場合の話で、一つのオブジェクトを.toString()
を使って文字列に変換すること自体は問題ありません。
AVOID using curly braces in interpolation when not needed.
文字列補完の際、不要な中カッコは使わないようにしましょう。
If you’re interpolating a simple identifier not immediately followed by more alphanumeric text, the {}
should be omitted.
直後に英数字のテキストが続かない単純な識別子を補間する場合は、{}を省略してください。
good↓
var greeting = 'Hi, $name! I love your ${decade}s costume.';
bad↓
var greeting = 'Hi, ${name}! I love your ${decade}s costume.';
Collections
Out of the box, Dart supports four collection types: lists, maps, queues, and sets. The following best practices apply to collections.
Dartは、List、Map、Queue、Setの4種類のコレクションをサポートしています。以下のベストプラクティスは、コレクションに適用されます。
DO use collection literals when possible.
可能な箇所ではコレクションリテラルを使いましょう。
Dartには3つの中核となるコレクションタイプがあります。List、Map、そしてSetです。MapとSetクラスには、ほとんどのクラスと同様に無名コンストラクタがあります。しかし、これらのコレクションは非常に頻繁に使用されるため、Dartにはこれらのインスタンスを生成するためのより良い組み込み構文があります。
good↓
var points = <Point>[];
var addresses = <String, Address>{};
var counts = <int>{};
bad↓
var addresses = Map<String, Address>();
var counts = Set<int>();
Note that this guideline doesn’t apply to the named constructors for those classes.
このガイドラインは名前付きコンストラクタには当てはまらないので注意してください。
List.from()
, Map.fromIterable()
, and friends all have their uses. (The List class also has an unnamed constructor, but it is prohibited in null safe Dart.)
List.from()、Map.fromIterable()など、
すべてその用途があります。(Listクラスには無名コンストラクタもありますが、ヌルセーフなDartでは禁止されています)。
Collection literals are particularly powerful in Dart because they give you access to the spread operator for including the contents of other collections, and if
and for
for performing control flow while building the contents:
コレクションリテラルは、他のコレクションのコンテンツを含めるため、およびコンテンツの構築中に制御フローを実行するためのスプレッド演算子にアクセスできるため、Dartで特に強力です。
good↓
var arguments = [
...options,
command,
...?modeFlags,
for (var path in filePaths)
if (path.endsWith('.dart')) path.replaceAll('.dart', '.js')
];
bad↓
var arguments = <String>[];
arguments.addAll(options);
arguments.add(command);
if (modeFlags != null) arguments.addAll(modeFlags);
arguments.addAll(filePaths
.where((path) => path.endsWith('.dart'))
.map((path) => path.replaceAll('.dart', '.js')));
goodサンプルとbadサンプルは同じ結果になります。
DON’T use .length
to see if a collection is empty.
The Iterable contract does not require that a collection know its length or be able to provide it in constant time.
Iterable契約(クラス)は、コレクションがその長さを知っていることや、一定の時間でそれを提供できることを要求しません。
Calling .length
just to see if the collection contains anything can be painfully slow.
コレクションに何か含まれているかどうかを確認するためだけに .length を呼び出すと、非常に時間がかかることがあります。
Instead, there are faster and more readable getters: .isEmpty
and .isNotEmpty
. Use the one that doesn’t require you to negate the result.
その代わり、より高速で読みやすいゲッター、.isEmpty と .isNotEmpty があります。結果を否定する必要がない方を使いましょう。
good↓
if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');
bad↓
if (lunchBox.length == 0) return 'so hungry...';
if (!words.isEmpty) return words.join(' ');
AVOID using Iterable.forEach()
with a function literal.
関数リテラルと一緒にIterable.forEach()
を使わない。
forEach()
functions are widely used in JavaScript because the built in for-in
loop doesn’t do what you usually want. In Dart, if you want to iterate over a sequence, the idiomatic way to do that is using a loop.
JavaScriptではforEach()関数は広く使用されています。なぜなら組み込みのfor-inループは通常あなたが期待することをしてくれないからです。Dartでイテラブルをイテレートしたい場合、ループ(for-in文)を使うのが一般的な方法です。
good↓
for (final person in people) {
...
}
bad↓
people.forEach((person) {
...
});
Note that this guideline specifically says “function literal”. If you want to invoke some already existing function on each element, forEach()
is fine.
このガイドラインでは「関数リテラル」と言っています。それぞれの要素に対して既に存在する関数を実行したい場合(下記のように)forEach()
メソッドを使っても問題ありません。
good↓
people.forEach(print);
Also note that it’s always OK to use Map.forEach()
. Maps aren’t iterable, so this guideline doesn’t apply.
それと、Map.forEach()メソッドは関数リテラルと一緒に使っても問題ありません。MapはIterableではありませんのでこのガイドラインは適用されません。
DON’T use List.from()
unless you intend to change the type of the result.
結果の型を変更する場合を除いて、List.from()を使用しないでください。
Given an Iterable, there are two obvious ways to produce a new List that contains the same elements:
あるIterableと同じ要素を持つListを新しく生成する方法は二つあります。
var copy1 = iterable.toList();
var copy2 = List.from(iterable);
The obvious difference is that the first one is shorter. The important difference is that the first one preserves the type argument of the original object:
パッと見てわかる違いとして一つ目の方が短いです。
重要な違いとして、一つ目は要素の型が(新しく生成されたListでも)保存されるということです。
good↓
// Creates a List<int>:
var iterable = [1, 2, 3];
// Prints "List<int>":
print(iterable.toList().runtimeType);
bad↓
// Creates a List<int>:
var iterable = [1, 2, 3];
// Prints "List<dynamic>":
print(List.from(iterable).runtimeType);
If you want to change the type, then calling List.from()
is useful:
なので、新しく生成するList(の要素の型)を、Iterable(の要素の型)とは別のものにしたい場合はList.from()
を使うのは有効です。
good↓
var numbers = [1, 2.3, 4]; // List<num>.
numbers.removeAt(1); // Now it only contains integers.
var ints = List<int>.from(numbers);
But if your goal is just to copy the iterable and preserve its original type, or you don’t care about the type, then use toList()
.
しかし、要素の型を保存する必要がある、または要素の型はどうでも良い場合はtoList()を使いましょう。
DO use whereType()
to filter a collection by type.
コレクションを型によってフィルタリングしたい場合whereType()メソッドを使いましょう。
Let’s say you have a list containing a mixture of objects, and you want to get just the integers out of it. You could use where()
like this:
intとStringが混在Listから、int型の要素のみを持つListを取得したい場合。下記のようにwhere()メソッドを使ってみるとどうでしょう?
var objects = [1, 'a', 2, 'b', 3];
var ints = objects.where((e) => e is int);
This is verbose, but, worse, it returns an iterable whose type probably isn’t what you want.
これは冗長ですが、もっと悪いことには、返されたIterableはあなたが望むものとは多分違うかもしれません。
In the example here, it returns an Iterable<Object>
even though you likely want an Iterable<int>
since that’s the type you’re filtering it to.
このサンプルでは、Iterable <int>が必要な場合でも、Iterable <Object>が返されます。これは、Iterable<Object>がフィルタリング対象のタイプであるためです。
void main() { var objects = [1, 'a', 2, 'b', 3]; print(objects.runtimeType); var ints = objects.where((e) => e is int); print(ints.runtimeType); print(ints); } //JSArray<Object> //WhereIterable<Object> //(1, 2, 3)
DartPadで動かしてみたら一応フィルターはできた。「intsの型がList<int>ではなくList<Object>である」点が期待する結果と違う、ということらしい。
Sometimes you see code that “corrects” the above error by adding cast()
:
たまに、上記のエラーをcast()メソッドを追加することで「修正」しているコードを目にすることがあるかもしれません。
bad↓
var objects = [1, 'a', 2, 'b', 3];
var ints = objects.where((e) => e is int).cast<int>();
That’s verbose and causes two wrappers to be created, with two layers of indirection and redundant runtime checking. Fortunately, the core library has the whereType()
method for this exact use case:
これは冗長であり、2つのラッパーが作成され、2層の間接参照と冗長なランタイムチェックが行われています。 幸いコアライブラリには、まさにこのユースケースのためのwhereType()メソッドがあります。
good↓
var objects = [1, 'a', 2, 'b', 3];
var ints = objects.whereType<int>();
Using whereType()
is concise, produces an Iterable of the desired type, and has no unnecessary levels of wrapping.
whereType()メソッドを使えば簡潔で、希望の型のイテラブルが取得でき、不必要なレベルのラッピングもありません。
DON’T use cast()
when a nearby operation will do.
Often when you’re dealing with an iterable or stream, you perform several transformations on it. At the end, you want to produce an object with a certain type argument. Instead of tacking on a call to cast()
, see if one of the existing transformations can change the type.
イテラブルやストリームを扱うとき、それに対していくつかの変換を行うことがよくあります。最終的に、特定の型引数を持つオブジェクトを生成したい場合です。cast()メソッドを使う代わりに、既存の変換のうちの1つ(つまりcast()メソッド以外の方法)で型を変更できるかどうかを確認しましょう。
簡単にいうと「他の方法があるならできるだけcast()メソッドは使わない方が良い」という事らしい。
If you’re already calling toList()
, replace that with a call to List<T>.from()
where T
is the type of resulting list you want.
もし既にtoList()メソッドを使っているなら、その箇所をList<T>.from()に置き換えてTをあなたの希望する型にしてください。
good↓
var stuff = <dynamic>[1, 2];
var ints = List<int>.from(stuff);
bad↓
var stuff = <dynamic>[1, 2];
var ints = stuff.toList().cast<int>();
If you are calling map()
, give it an explicit type argument so that it produces an iterable of the desired type. Type inference often picks the correct type for you based on the function you pass to map()
, but sometimes you need to be explicit.
もしあなたがmap()メソッドを使っているなら、希望する型のイテラブルを生成するために、明示的に型引数を追加してください。型推測によって希望する型が選択されることもあるのですが、時にはあなたが自分で明示的に型引数によって型を示す必要があるケースもあります。
good↓
var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map<double>((n) => 1 / n);
bad↓
var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map((n) => 1 / n).cast<double>();
AVOID using cast()
.
This is the softer generalization of the previous rule. Sometimes there is no nearby operation you can use to fix the type of some object. Even then, when possible avoid using cast()
to “change” a collection’s type.
これは、前のルールのよりソフトな一般化です。 オブジェクトのタイプを修正するために使用できる、castメソッド代替となる操作がわからない場合があります。 それでも、可能であれば、cast()を使用してコレクションのタイプを「変更」することは避けてください。
Prefer any of these options instead:
代わりに下記の選択肢のほうが好ましいです。
- Create it with the right type. Change the code where the collection is first created so that it has the right type.
一番最初にコレクションが生成される箇所を書き直して、正しい型のコレクションが生成されるようにする。
- Cast the elements on access. If you immediately iterate over the collection, cast each element inside the iteration.
アクセス時に要素をキャストします。 コレクションをすぐに反復する場合は、反復内の各要素をキャストします。
Eagerly cast using List.from()
. If you’ll eventually access most of the elements in the collection, and you don’t need the object to be backed by the original live object, convert it using List.from()
.
最終的にコレクション内のほとんどの要素にアクセスし、オブジェクトを元のライブオブジェクトでバックアップする必要がない場合は、List.from()を使用して変換します。
The cast()
method returns a lazy collection that checks the element type on every operation.
cast()メソッドは遅延でコレクションを返し、処理の度に要素の型をチェックします。
If you perform only a few operations on only a few elements, that laziness can be good. But in many cases, the overhead of lazy validation and of wrapping outweighs the benefits.
少数の要素に対して少数の操作のみを実行する場合、その遅延は良い場合があります。 しかし、多くの場合、遅延検証とラッピングのオーバーヘッドがメリットを上回ります。
Here is an example of creating it with the right type:
期待する型を生成する方法のサンプルを示します。
good↓
List<int> singletonList(int value) {
var list = <int>[];
list.add(value);
return list;
}
bad↓
List<int> singletonList(int value) {
var list = []; // List<dynamic>.
list.add(value);
return list.cast<int>();
}
Here is casting each element on access:
各要素にアクセスしてキャストする方法です。
good↓
void printEvens(List<Object> objects) {
// We happen to know the list only contains ints.
//リストにはintしか含まれていないことがわかりました。
for (final n in objects) {
if ((n as int).isEven) print(n);
}
}
bad↓
void printEvens(List<Object> objects) {
// We happen to know the list only contains ints.
for (final n in objects.cast<int>()) {
if (n.isEven) print(n);
}
}
Here is casting eagerly using List.from()
:
List.from()を使用したキャストの例です。
good↓
int median(List<Object> objects) {
// We happen to know the list only contains ints.
//リストにはintしか含まれていないことがわかりました。
var ints = List<int>.from(objects);
ints.sort();
return ints[ints.length ~/ 2];
}
bad↓
int median(List<Object> objects) {
// We happen to know the list only contains ints.
var ints = objects.cast<int>();
ints.sort();
return ints[ints.length ~/ 2];
}
These alternatives don’t always work, of course, and sometimes cast()
is the right answer. But consider that method a little risky and undesirable—it can be slow and may fail at runtime if you aren’t careful.
もちろこれらの代替法は常に機能するとは限りませんし、cast()メソッドの使用が正解のケースもあります。しかし、cast()メソッドは少しリスクが高く好ましくないものであると考えてください。遅くなるケース、あるいは不注意があると実行時エラーが出るケースが考えられます。
上記のサンプルコードのコメントにある通り、あくまでもすべての要素がint型だとわかっているのでasやList.fromでのキャストが可能。int型以外の要素をint型にキャストしようとすると下記のようにエラーになる。
//DartPad void main() { Object obj1 = 4; int int1 = obj1 as int; print(int1);//4 /* Object obj2 = 'str'; int int2 = obj2 as int; print(int2);//Script error. */ Object obj3 = 6; List<int> list3 = List<int>.from([obj3]); print(list3);//[6] Object obj4 = 'str'; List<int> list4 = List<int>.from([obj4]); print(list4);//Script error. }
参考