実装 vs 継承
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 47/110。
extends と implements の両方を理解したところで、それぞれをいつ使うべきかを明確にしましょう。
振る舞いを継承したいときは、extendsを使用します。サブクラスは親のコードをすべてそのまま受け継ぎ、それを基に機能を拡張することができます:
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // 継承されているため、自動的に動作します
dog.bark();
}コードを継承せずに構造を保証したい場合は、implementsを使用します。すべてのメソッドを自分自身で記述する必要があります:
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...'); // 完全にカスタムな実装
}
}主な違いは、extends が「私はこれの一種です」と言うのに対し、implements は「私はこれができることを実行できます」と言う点です。Dog は Animal であり、同じ方法で呼吸します。Robot は動物ではありませんが、独自のロジックで同じコントラクトを満たすことができます。
覚えておいてください:継承できるクラスは1つだけですが、複数のインターフェースを実装することができます。真の継承関係には extends を選択し、柔軟性が必要な場合や、実装の詳細を共有せずに規約を強制したい場合には implements を選択してください。
チャレンジ
簡単extends と implements をいつ使うべきかを示すワーカーシステムを構築しましょう。ベースとなるワーカークラスを作成し、2つのアプローチを示します。1つは真にワーカー「である(is a)」クラスで振る舞いを継承し、もう1つはワーカーがすることを「できる(can do)」クラスですが、完全にカスタムのロジックを持ちます。
コードを2つのファイルに整理します:
worker.dart: ここでワーカーの階層を定義します:String nameプロパティとコンストラクタを持つWorkerクラス。[name] is working...と出力するwork()メソッドと、[name] takes a coffee breakと出力するtakeBreak()メソッドを含めます。Workerを extends(継承)するOfficeWorkerクラス。String departmentプロパティを追加します。work()をオーバーライドして[name] is typing reports in [department]と出力するようにしますが、継承されたtakeBreak()の振る舞いはそのまま保持します。Workerを implements(実装)するRobotWorkerクラス。String modelプロパティを追加します。ロボットは厳密にはワーカーではありませんが、ワーカーと同じことができるため、完全にカスタムの実装を提供します。work()はRobot [model] is assembling partsと出力し、takeBreak()はRobot [model] is rechargingと出力するようにします。
main.dart: ワーカーファイルをインポートし、両方のアプローチを実演します:'Marketing'部署に'Alice'という名前のOfficeWorkerを作成します。- モデル名
'RX-7'のRobotWorkerを作成します。 - オフィスワーカーに対して、
work()を呼び出し、次にtakeBreak()を呼び出します。 - 空行を1行出力します。
- ロボットワーカーに対して、
work()を呼び出し、次にtakeBreak()を呼び出します。
OfficeWorker は Worker を継承(extends)しているため、自動的に takeBreak() を継承していることに注目してください。これは真にワーカー「である」からです。一方、RobotWorker はインターフェースを実装(implements)しているだけなので、すべてのメソッドに対して独自の実装を提供する必要があります。これはワーカーができることを「できる」のですが、ロボット独自の方法で行うためです。
期待される出力:
Alice is typing reports in Marketing
Alice takes a coffee break
Robot RX-7 is assembling parts
Robot RX-7 is rechargingチートシート
親クラスから振る舞いを継承するには extends を使用します。サブクラスは自動的にすべての親メソッドを取得し、それらをオーバーライドできます:
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Inherited automatically
dog.bark();
}コードを継承せずに構造を保証するには implements を使用します。すべてのメソッドに対して独自の定義(実装)を提供する必要があります:
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...');
}
}主な違い:
extends:「私はこれの一種です」 - コードを共有する真の継承implements:「私はこれができることと同じことができます」 - カスタムロジックによる契約の強制- 継承(extend)できるクラスは1つだけですが、複数のインターフェースを実装(implement)できます。
振る舞いを継承すべき真の親子関係には extends を選択してください。柔軟性が必要な場合や、実装を共有せずに契約を強制したい場合は implements を選択してください。
自分で試してみよう
import 'worker.dart';
void main() {
// TODO: 'Marketing'部門の'Alice'という名前のOfficeWorkerを作成してください
// TODO: モデル名'RX-7'のRobotWorkerを作成してください
// TODO: office workerに対して、work()を呼び出し、次にtakeBreak()を呼び出してください
// TODO: 空行をプリントしてください
// TODO: robot workerに対して、work()を呼び出し、次にtakeBreak()を呼び出してください
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ