クラス修飾子
クラス修飾子は、クラスまたはミックスインがどのように使用できるかを制御します。これは、自身のライブラリ内からと、それが定義されているライブラリの外部からの両方です。
修飾子キーワードは、クラスまたはミックスイン宣言の前に記述します。たとえば、abstract class
と記述すると抽象クラスが定義されます。クラス宣言の前に記述できる修飾子の完全なセットには、次のようなものがあります。
abstract
base
final
interface
sealed
mixin
base
修飾子のみがミックスイン宣言の前に記述できます。修飾子は、enum
、typedef
、extension
、または extension type
のような他の宣言には適用されません。
クラス修飾子を使用するかどうかを決定する際には、クラスの意図された用途と、クラスが依存できる必要のある動作を考慮してください。
修飾子なし
#任意のライブラリから構築またはサブタイプ化するための無制限の許可を与えるには、修飾子なしで class
または mixin
宣言を使用します。デフォルトでは、次のことができます。
- クラスの新しいインスタンスを構築します。
- クラスを拡張して新しいサブタイプを作成します。
- クラスまたはミックスインのインターフェースを実装します。
- ミックスインまたはミックスインクラスをミックスインします。
abstract
#インターフェース全体の完全な具象実装を必要としないクラスを定義するには、abstract
修飾子を使用します。
抽象クラスは、それが自身のライブラリであろうと外部のライブラリであろうと、どのライブラリからも構築できません。抽象クラスには、しばしば抽象メソッドがあります。
abstract class Vehicle {
void moveForward(int meters);
}
import 'a.dart';
// Error: Can't be constructed.
Vehicle myVehicle = Vehicle();
// Can be extended.
class Car extends Vehicle {
int passengers = 4;
@override
void moveForward(int meters) {
// ...
}
}
// Can be implemented.
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}
抽象クラスをインスタンス化可能に見せたい場合は、ファクトリーコンストラクタを定義します。
base
#クラスまたはミックスインの実装の継承を強制するには、base
修飾子を使用します。base クラスは、自身のライブラリの外部での実装を許可しません。これにより、次のことが保証されます。
- クラスのサブタイプのインスタンスが作成されるたびに、base クラスのコンストラクタが呼び出されます。
- 実装されたすべてのプライベートメンバーは、サブタイプに存在します。
base
クラスに新しい実装されたメンバーを追加しても、すべてのサブタイプが新しいメンバーを継承するため、サブタイプは壊れません。- これは、サブタイプがすでに同じ名前と互換性のないシグネチャを持つメンバーを宣言している場合を除きます。
base クラスを実装または拡張するクラスは、base
、final
、または sealed
としてマークする必要があります。これにより、外部ライブラリが base クラスの保証を破るのを防ぎます。
base class Vehicle {
void moveForward(int meters) {
// ...
}
}
import 'a.dart';
// Can be constructed.
Vehicle myVehicle = Vehicle();
// Can be extended.
base class Car extends Vehicle {
int passengers = 4;
// ...
}
// ERROR: Can't be implemented.
base class MockVehicle implements Vehicle {
@override
void moveForward() {
// ...
}
}
interface
#インターフェースを定義するには、interface
修飾子を使用します。インターフェースを定義するライブラリの外部のライブラリは、インターフェースを実装できますが、拡張することはできません。これにより、次のことが保証されます。
- クラスのインスタンスメソッドの 1 つが
this
上の別のインスタンスメソッドを呼び出すと、常に同じライブラリのメソッドの既知の実装を呼び出します。 - 他のライブラリは、インターフェースクラス自身のメソッドが後で予期しない方法で呼び出す可能性のあるメソッドをオーバーライドできません。これにより、壊れやすいベースクラスの問題が軽減されます。
interface class Vehicle {
void moveForward(int meters) {
// ...
}
}
import 'a.dart';
// Can be constructed.
Vehicle myVehicle = Vehicle();
// ERROR: Can't be inherited.
class Car extends Vehicle {
int passengers = 4;
// ...
}
// Can be implemented.
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}
abstract interface
#interface
修飾子の最も一般的な使用法は、純粋なインターフェースを定義することです。interface
修飾子と abstract
修飾子を 組み合わせると、abstract interface class
になります。
interface
クラスと同様に、他のライブラリは純粋なインターフェースを実装できますが、継承することはできません。abstract
クラスと同様に、純粋なインターフェースは抽象メンバーを持つことができます。
final
#型階層を閉じるには、final
修飾子を使用します。これにより、現在のライブラリの外部からのクラスのサブタイプ化が防止されます。継承と実装の両方を禁止すると、サブタイプ化が完全に防止されます。これにより、次のことが保証されます。
- API に段階的な変更を安全に追加できます。
- サードパーティのサブクラスで上書きされていないことを知って、インスタンスメソッドを安全に呼び出すことができます。
final クラスは、同じライブラリ内で拡張または実装できます。final
修飾子は base
の効果を包含するため、サブクラスも base
、final
、または sealed
としてマークする必要があります。
final class Vehicle {
void moveForward(int meters) {
// ...
}
}
import 'a.dart';
// Can be constructed.
Vehicle myVehicle = Vehicle();
// ERROR: Can't be inherited.
class Car extends Vehicle {
int passengers = 4;
// ...
}
class MockVehicle implements Vehicle {
// ERROR: Can't be implemented.
@override
void moveForward(int meters) {
// ...
}
}
sealed
#既知の列挙可能なサブタイプのセットを作成するには、sealed
修飾子を使用します。これにより、網羅的であることが静的に保証されるこれらのサブタイプに対する switch を作成できます。
sealed
修飾子は、クラスが自身のライブラリの外部で拡張または実装されるのを防ぎます。sealed クラスは暗黙的に抽象です。
- 自身で構築することはできません。
- ファクトリーコンストラクタを持つことができます。
- サブクラスが使用するためのコンストラクタを定義できます。
ただし、sealed クラスのサブクラスは、暗黙的に抽象ではありません。
コンパイラは、同じライブラリにのみ存在できるため、可能な直接のサブタイプを認識しています。これにより、switch がケース内のすべての可能なサブタイプを網羅的に処理しない場合に、コンパイラが警告を発することができます。
sealed class Vehicle {}
class Car extends Vehicle {}
class Truck implements Vehicle {}
class Bicycle extends Vehicle {}
// ERROR: Can't be instantiated.
Vehicle myVehicle = Vehicle();
// Subclasses can be instantiated.
Vehicle myCar = Car();
String getVehicleSound(Vehicle vehicle) {
// ERROR: The switch is missing the Bicycle subtype or a default case.
return switch (vehicle) {
Car() => 'vroom',
Truck() => 'VROOOOMM',
};
}
網羅的な切り替えを望まない場合、または API を壊すことなく後でサブタイプを追加できるようにしたい場合は、final
修飾子を使用します。より詳細な比較については、sealed
と final
の比較を参照してください。
修飾子の組み合わせ
#一部の修飾子を組み合わせて、階層化された制限を行うことができます。クラス宣言は、次の順序で記述できます。
- (オプション)
abstract
。クラスが抽象メンバーを含めることができるかどうかを記述し、インスタンス化を防ぎます。 - (オプション)
base
、interface
、final
、またはsealed
のいずれか 1 つ。他のライブラリがクラスをサブタイプ化する際の制限を記述します。 - (オプション)
mixin
。宣言をミックスインできるかどうかを記述します。 class
キーワード自体。
一部の修飾子は、矛盾している、冗長である、または相互に排他的であるため、組み合わせることはできません。
abstract
とsealed
。sealed クラスは暗黙的に abstract です。interface
、final
、またはsealed
とmixin
。これらのアクセス修飾子は、ミックスインを防ぎます。
クラス修飾子の組み合わせ方に関する詳細なガイダンスについては、クラス修飾子リファレンスを参照してください。
特に明記されていない限り、このサイトのドキュメントは Dart 3.5.3 を反映しています。ページの最終更新日は 2024 年 6 月 24 日です。 ソースを表示 または 問題を報告する。