メインコンテンツにスキップ

用語集

以下は、Dart のドキュメント全体で使用される用語の定義です。

アシスト

コードに対する一般的な改善を目的とした、自動化されたローカルなコード編集。

アシストとは、コードに対する一般的な改善を目的とした、自動化されたローカルなコード編集のことです。アシストの例としては、switch 文を switch 式に変換する、if 文の then ブロックと else ブロックを反転させる、ウィジェット構造にウィジェットを挿入するなどがあります。

関連ドキュメントとリソース

定数コンテキスト

const キーワードが暗黙的に適用され、その領域内のすべてのものが定数でなければならないコード領域。

定数コンテキストとは、その領域内のすべてが定数であることが要求されるという事実によって const キーワードを含める必要がないコード領域のことです。定数コンテキストには以下の場所があります。

  • const キーワードでプレフィックスされたリスト、マップ、またはセットリテラルの内部のすべて。例:

    dart
    var l = const [/*constant context*/];
  • 定数コンストラクタの呼び出し内の引数。例:

    dart
    var p = const Point(/*constant context*/);
  • const キーワードでプレフィックスされた変数の初期化。例:

    dart
    const v = /*constant context*/;
  • アノテーション。

  • case 句の式。例:

    dart
    void f(int e) {
      switch (e) {
        case /*constant context*/:
          break;
      }
    }

確定代入

変数が使用される前に値が確定的に代入されているかどうかを判断すること。

確定代入分析とは、コードの各ポイントにおける各ローカル変数について、以下のいずれかが真であるかを判断するプロセスです。

  • 変数は確定的に値が代入されている(確定代入済み)。
  • 変数は確定的に値が代入されていない(確定未代入)。
  • 変数は、そこに至る実行パスによって値が代入されているかどうかが異なる可能性がある。

確定代入分析は、値が代入されていない可能性がある変数が参照されている場所や、一度しか値が代入できない変数が既に代入されている可能性がある後に代入されている場所など、コードの問題を見つけるのに役立ちます。

たとえば、以下のコードでは、変数 sprint の引数として渡されるときに確定未代入です。

dart
void f() {
  String s;
  print(s);
}

しかし、以下のコードでは、変数 s は確定代入されています。

dart
void f(String name) {
  String s = 'Hello $name!';
  print(s);
}

確定代入分析は、複数の実行パスがある場合でも、変数が確定代入されている(または未代入である)かどうかを判断できます。以下のコードでは、if 文の true または false ブランチのどちらを通っても print 関数が呼び出されますが、どちらのブランチを通っても s に値が代入されるため、print に渡される前に確定代入されています。

dart
void f(String name, bool casual) {
  String s;
  if (casual) {
    s = 'Hi $name!';
  } else {
    s = 'Hello $name!';
  }
  print(s);
}

フロー分析では、if 文の終端は結合と呼ばれます。これは、2 つ以上の実行パスが再び合流する場所です。結合がある場合、分析では、マージされているすべてのパスに沿って変数が確定代入されている場合は確定代入済み、すべてのパスに沿って変数が確定未代入である場合は確定未代入であると判断します。

場合によっては、変数が一方のパスで値が代入され、もう一方のパスでは代入されないことがあります。この場合、変数は値が代入されているかどうかが不明です。次の例では、if 文の true ブランチが実行されるかどうかわからないため、変数が値に代入されているかどうかが不明です。

dart
void f(String name, bool casual) {
  String s;
  if (casual) {
    s = 'Hi $name!';
  }
  print(s);
}

s に値を代入しない false ブランチがある場合も同様です。

ループの分析はもう少し複雑ですが、基本的な考え方は同じです。たとえば、while ループの条件は常に実行されますが、本体は実行される場合とされない場合があります。したがって、if 文と同様に、while 文の終端には、条件が true のパスと条件が false のパスとの間に結合があります。

関数

トップレベル関数、ローカル関数、静的メソッド、インスタンスメソッドを総称する用語。

不否定パターン

常に一致するパターン。

不否定パターンとは、常に一致するパターンです。不否定パターンは、不否定コンテキストにのみ出現できます。これらは、宣言代入のパターンコンテキストです。

late

変数の初期化を遅延させることを可能にし、通常は非 null 許容変数と共に使用されるキーワード。

Dart の late キーワードは、変数が宣言後に、しかし使用される前に初期化されることを示すために使用されます。これにより、値が確実に受け取られることがわかっていても、すぐにではない場合に、変数を null 許容(?)にする必要がなくなります。

late を使用すると初期化が遅延され、特に依存関係や複雑なセットアップを扱う場合に、より柔軟で読みやすいコードを書くことができます。

dart
late String description;

void setup() {
  description = 'This will be initialized before use.';
}

公開 API の一部である late 変数には注意してください。クライアントが初期化される前に変数にアクセスすると、LateInitializationError が発生しますが、これはほとんどコンテキストを提供しません。このような場合は、プライベートな null 許容変数と、早期にアクセスされた場合に記述的なエラー(例:StateError)をスローする公開ゲッターの使用を検討してください。これにより、複雑さが増すにもかかわらず、API ユーザーに明確なフィードバックを提供できます。

変数が一度だけ設定されるべき場合は、late final も使用できます。これは、オブジェクト構築時には値が利用できないシナリオ、たとえばオブジェクトグラフにおける循環依存関係などに便利です。

dart
class LinkedQueue<T> {
  late final QueueLink<T> _head;
  
  LinkedQueue() {
    _head = QueueLink<T>._head(owner: this); // Cyclic reference between objects
  }
}

注意:late 変数が初期化される前にアクセスされたり、まったく初期化されなかったりすると、実行時エラーが発生します。

ミックスイン応用

ミックスインがクラスに適用されたときに作成されるクラス。

ミックスイン応用とは、ミックスインがクラスに適用されたときに作成されるクラスのことです。たとえば、次の宣言を考えてみましょう。

dart
class A {}

mixin M {}

class B extends A with M {}

クラス B は、A への M のミックスイン応用のサブクラスであり、これは A+M とも呼ばれます。クラス A+MA のサブクラスであり、M からコピーされたメンバーを持ちます。

ミックスイン応用を実際の名前で指定するには、次のように定義します。

dart
class A {}

mixin M {}

class A_M = A with M;

この A_M の宣言を考慮すると、次の B の宣言は、元の例の B の宣言と同等です。

dart
class B extends A_M {}

関連ドキュメントとリソース

オーバーライド推論

メソッド宣言で不足している型がどのように推論されるか。

オーバーライド推論とは、メソッド宣言で不足している型が、オーバーライドするメソッドに対応する型に基づいて推論されるプロセスです。

候補メソッド(型情報が不足しているメソッド)が単一の継承メソッドをオーバーライドする場合、オーバーライドされたメソッドの対応する型が推論されます。たとえば、次のコードを考えてみましょう。

dart
class A {
  int m(String s) => 0;
}

class B extends A {
  @override
  m(s) => 1;
}

Bm の宣言は、戻り値の型と引数の型の両方が不足しているため、候補です。単一のメソッド(A のメソッド m)をオーバーライドするため、オーバーライドされたメソッドの型が不足している型を推論するために使用され、B のメソッドは int m(String s) => 1; として宣言されていたかのように扱われます。

候補メソッドが複数のメソッドをオーバーライドし、それらのオーバーライドされたメソッドの 1 つの関数型 Ms が、他のすべてのオーバーライドされたメソッドの関数型のスーパータイプである場合、Ms が不足している型を推論するために使用されます。たとえば、次のコードを考えてみましょう。

dart
class A {
  int m(num n) => 0;
}

class B {
  num m(int i) => 0;
}

class C implements A, B {
  @override
  m(n) => 1;
}

Cm の宣言は、戻り値の型と引数の型の両方が不足しているため、オーバーライド推論の候補です。これは AmBm の両方をオーバーライドするため、コンパイラは不足している型を推論できるいずれかを選択する必要があります。しかし、Am の関数型(int Function(num))が Bm の関数型(num Function(int))のスーパータイプであるため、A の関数が不足している型を推論するために使用されます。結果は、C のメソッドを int m(num n) => 1; として宣言した場合と同じになります。

オーバーライドされたメソッドのいずれにも、他のすべてのオーバーライドされたメソッドのスーパータイプである関数型がない場合は、エラーとなります。

関連ドキュメントとリソース

パートファイル

part of ディレクティブを含む Dart ソースファイル。

パートファイルとは、part of ディレクティブを含む Dart ソースファイルであり、part ディレクティブを使用してライブラリに含まれるファイルのことです。

潜在的な非 null 許容

明示的に非 null 許容であるか、型パラメータであるために非 null 許容である型。

型が潜在的な非 null 許容であるとは、明示的に非 null 許容であるか、または型パラメータである場合を指します。

型が明示的に非 null 許容であるとは、疑問符(?)で後に続かない型名である場合を指します。Nulldynamic のように常に null 許容である型がいくつか存在すること、および FutureOr は、疑問符で後に続かずかつ型引数が非 null 許容である場合(例:FutureOr<String>)にのみ非 null 許容となることに注意してください。

型パラメータは、実際の実行時型(型引数として指定された型)が非 null 許容である可能性があるため、潜在的な非 null 許容です。たとえば、class C<T> {} の宣言を与えると、型 C は、C<int> のように、非 null 許容の型引数と共に使用できます。

関連ドキュメントとリソース

公開ライブラリ

パッケージの lib ディレクトリに配置されているが、lib/src ディレクトリ内にはないライブラリ。

公開ライブラリとは、パッケージの lib ディレクトリ内に配置されているが、lib/src ディレクトリ内にはないライブラリのことです。

クイックフィックス

特定の診断によって報告された問題を修正することを目的とした、自動化されたローカルなコード編集。

リファクタリング

非ローカルまたはユーザーの対話を必要とする変更を対象としたコード編集。

リファクタリングとは、非ローカルまたはユーザーの対話を必要とする変更を対象としたコード編集のことです。リファクタリングの例としては、名前の変更、削除、コードの抽出などがあります。

関連ドキュメントとリソース

拒否可能パターン

値をテストできるパターン。

拒否可能パターンとは、パターンが値と一致するかどうかを判断するために、値に対してテストできるパターンです。一致しない場合、パターンは一致を拒否します。拒否可能パターンは、マッチングコンテキストに出現します。

サブクラス

別のクラスの実装を継承するクラス。

サブクラスとは、extends キーワードを使用するか、ミックスイン応用によって、別のクラスの実装を継承するクラスです。

dart
// A is a subclass of B; B is the superclass of A.
class A extends B {}

// B1 has the superclass `A with M`, which has the superclass A.
class B1 extends A with M {}

サブクラス関係は、関連するサブタイプ関係も示唆します。たとえば、class A は、暗黙的に関連する型 A を定義し、クラス A のインスタンスがそれを格納します。したがって、class A extends B は、クラス AB のサブクラスであると宣言するだけでなく、 A Bサブタイプであることを確立します。

サブクラス関係は、サブタイプ関係のサブセットです。ドキュメントで「ST のサブタイプでなければならない」と述べられている場合、ST のサブクラスであっても問題ありません。ただし、逆は真ではありません。すべてのサブタイプがサブクラスであるわけではありません。

関連ドキュメントとリソース

サブタイプ

スーパータイプの値が期待される場所で使用できる型。

サブタイプ関係とは、ある型の値が、スーパータイプである別の型の値が期待される場所で代用できる関係です。たとえば、ST のサブタイプである場合、型 S の値を型 T が期待される場所に代用できます。

サブタイプは、スーパータイプのすべての操作(および場合によっては追加の操作)をサポートします。実際には、サブタイプの値をスーパータイプを期待する任意の場所に代入でき、スーパータイプのすべてのメソッドはサブタイプで利用可能です。

これは少なくとも静的に真です。具体的な API では、実行時に代用が許可されない場合があります。これは、その操作に依存します。

一部のサブタイプ関係は、型(たとえば、intint? のサブタイプです)や関数型(たとえば、String Function()void Function() のサブタイプです)の構造に基づいています。

サブタイプは、実装または継承(直接または間接)によってクラスにも導入されることがあります。

dart
// A is a subtype of B, but NOT a subclass of B.
class A implements B {}

// C is a subtype AND a subclass of D.
class C extends D {}

関連ドキュメントとリソース

共変性と共変位置

型の型引数を変更すると、元の型と結果の型との関係にどのように影響するか。

Dart では、型宣言(クラスなど)や関数の戻り値の型引数を変更すると、型全体の関係が同じ方向に(共変に)変化します。

しかし、関数の引数型の型を変更すると、型全体の関係が反対方向に(反変に)変化します。

クラス(またはミックスインなどの他の型宣言)の型パラメータは、型全体が実際の型引数と「共変する」場合に共変であると言われます。つまり、型引数がサブタイプで置き換えられると、型全体もサブタイプになります。

たとえば、List クラスの型パラメータは共変です。なぜなら、リスト型は型引数と共変するからです。intObject のサブタイプであるため、List<int>List<Object> のサブタイプです。

Dart では、すべてのクラス、ミックスイン、ミックスインクラス、列挙型の宣言の型パラメータは共変です。

しかし、関数型は異なります。関数型は戻り値の型では共変ですが、引数型では逆(反変と呼ばれる)です。たとえば、型 int Function(int) は型 Object Function(int) のサブタイプですが、int Function(Object) のスーパータイプです。

これは、代用可能性を考慮すると理にかなっています。静的型が int Function(int) の関数を呼び出す場合、その関数は実際には実行時に int Function(Object) の型である可能性があります。静的型に基づくと、それを渡すことができる int を期待します。これは、関数が実際には任意の Object を受け入れるため、int 型のすべてのオブジェクトが含まれるため、問題ありません。同様に、返される結果は int 型になり、これも静的型に基づいて期待されるものです。

したがって、int Function(Object)int Function(int) のサブタイプです。

特に、関数型の間のこのサブタイプ関係は、引数型でのサブタイプ関係が存在する必要があることに注意してください。たとえば、intObject のサブタイプであるため、void Function(Object)void Function(int) のサブタイプです。

List<void Function(int)> のようなより複雑な型の場合、型の位置を考慮する必要があります。これを実現するには、型のいずれかの部分をプレースホルダーに変換し、その位置に異なる型が配置されたときに型に何が起こるかを検討します。

たとえば、プレースホルダー _ の代わりに異なる型を配置できる型のテンプレートとして List<void Function(_)> を考えてみましょう。この型は、プレースホルダーが発生する位置では反変です。

以下は、_ の代わりに Objectint を代入してこれを例示しています。List<void Function(Object)>List<void Function(int)> のサブタイプです。なぜなら、void Function(Object)void Function(int) のサブタイプだからです。これは、戻り値の型は void であり、引数の型(逆順)は intObject のサブタイプだからです。したがって、_ の位置の型は、List<void Function(_)> という型全体とは逆の方向に変化し、この「逆の方向」は定義により反変位置となります。

共変位置は同様に定義されます。たとえば、_List<_> 型の共変位置にあり、__ Function(int) 型の共変位置にもあります。

不変と呼ばれる別の種類の位置もありますが、これははるかにまれに発生するため、ここでは詳細を省略します。

実際には、クラス、ミックスインなどの型引数は共変位置にあり、関数型の戻り値の型も共変位置にありますが、引数型は反変位置にあることを知っておけば十分な場合が多いです。

ワイルドカード

パターンやその他のコンテキストで未使用の値を示すために変数名の代わりに使われる記号(_)。

ワイルドカードとは、値を無視するために使用されるアンダースコア文字(_)または値が意図的に未使用であることを示すために使用される文字のことです。パターン、デストラクト、スイッチ式で、値を名前に関連付けずに任意の値を一致させるためによく使用されます。

ワイルドカードは、特定のコンテキストで必要のない値を明確にマークすることで、コードをより意図的にします。

dart
// Ignoring the value in a for-each loop.
var names = ['Alice', 'Bob', 'Charlie'];
for (var _ in names) {
  print('Someone is here!');
}

ワイルドカードパターンは、特に以下の場合に役立ちます。

  • デストラクトされた値の特定のパーツのみが必要な場合。
  • 一部の値が無視されていることを明示的に示したい場合。
  • パターンマッチングで網羅的なケースが必要な場合。

ゾーン

非同期コード自体を変更せずに、非同期コードの動作をカスタマイズするためのメカニズム。

ゾーンとは、タイマー、マイクロタスク、未捕捉エラーなどの非同期イベントに対してカスタマイズされた動作でコードを実行できる実行コンテキストのことです。

ゾーンは以下に役立ちます。

  • ログ記録
  • エラー追跡
  • 非同期ギャップを越えてリクエスト固有の状態を維持する(たとえば、サーバーアプリで)
  • 非同期動作のテストとデバッグ

ゾーンは、非同期コードがそれを意識する必要なしに、非同期実行を追跡および影響を与える方法を提供します。

runZoned(または runZonedGuarded)を使用して新しいゾーンを作成し、エラー処理やタイマーなどのゾーン固有の動作をオーバーライドできます。print でさえオーバーライドできますが、非同期ではないため、利便性のために含まれています。

dart
import 'dart:async';

void main() {
  runZonedGuarded(() {
    Future.delayed(Duration(seconds: 1), () {
      throw 'Zone caught this error!';
    });
  }, (error, stackTrace) {
    print('Caught error: $error');
  });
}

前の例では、非同期コールバック内の未捕捉エラーはカスタムゾーンによってインターセプトされます。