抽象メソッド
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 44/110。
抽象メソッドは、本体を持たない抽象クラス内で宣言されたメソッドです。これはメソッドのシグネチャを定義しますが、実装は各サブクラスに委ねられます。抽象クラスを継承するクラスは、すべての抽象メソッドをオーバーライドしなければなりません。そうしない場合、そのクラスも抽象クラスになります。
abstract class Animal {
// 抽象メソッド - ボディがなく、サブクラスで実装する必要があります
String makeSound();
// 具象メソッド - ボディがあり、すべてのサブクラスで共有されます
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
@override
String makeSound() {
return 'Woof!'; // 実装を提供する必要があります
}
}
class Cat extends Animal {
@override
String makeSound() {
return 'Meow!'; // 異なる実装、同じシグネチャ
}
}サブクラスが抽象メソッドの実装を忘れた場合、Dartはコンパイルエラーを発生させます。これこそが重要な点です。抽象メソッドは契約を強制します。すべてのサブクラスはそのメソッドを持つことが保証されるため、抽象型のどのインスタンスに対しても安全にそのメソッドを呼び出すことができます。
// 抽象型を変数型として使用できます
Animal a = Dog();
print(a.makeSound()); // ワンワン!
Animal b = Cat();
print(b.makeSound()); // ニャー!抽象メソッドは、抽象クラス内の具象メソッドとは異なります。具象メソッドはボディを持ち、そのまま継承されます。抽象メソッドはボディを持たず、必ずオーバーライドされる必要があります。
同じ抽象クラス内で両方を混在させることで、一部の振る舞いを共有しつつ、他の振る舞いについてはサブクラスに独自の定義を強制することができます。
| 特徴 | 抽象メソッド | 具象メソッド |
|---|---|---|
| ボディの有無 | なし | あり |
| オーバーライド必須 | はい | いいえ |
| 目的 | 規約の強制 | 振る舞いの共有 |
| 定義場所 | 抽象クラスのみ | 任意のクラス |
チャレンジ
簡単抽象メソッドの実際の動作を示す図形計算機を作成しましょう。各図形が異なる方法で実装する必要がある抽象メソッドを持つ抽象クラスを定義し、それらの図形を共通のインターフェースを通じて使用します。
コードを4つのファイルに整理します:
shape.dart:finalフィールドcolor(String) とそれを初期化するコンストラクタを持つ抽象クラスShapeを定義します。2つの抽象メソッドdouble area()とdouble perimeter()を宣言します。また、A [color] shape with area: [area] and perimeter: [perimeter]と出力する具体的なメソッドdescribe()を追加します。area()とperimeter()は抽象メソッドであるため、各サブクラスが独自の計算を提供しますが、describe()はオーバーライドされることなくすべてのサブクラスで動作します。circle.dart:Shapeを継承するCircleクラスを作成します。finalフィールドradius(double) を持ちます。そのコンストラクタはcolorとradiusを受け取り、colorをsuperに渡します。area()を実装して3.14 * radius * radiusを返し、perimeter()を実装して2 * 3.14 * radiusを返すようにします。rectangle.dart:Shapeを継承するRectangleクラスを作成します。finalフィールドwidthとheight(ともに double) を持ちます。そのコンストラクタはcolor、width、heightを受け取り、colorをsuperに渡します。area()を実装してwidth * heightを返し、perimeter()を実装して2 * (width + height)を返すようにします。main.dart: すべてのファイルをインポートします。color が'red'で radius が5.0のCircleと、color が'blue'で width が4.0、height が6.0のRectangleを作成します。各図形でdescribe()を呼び出します。次に、両方の図形を含むList<Shape>を作成し、リストをループして、各図形のArea: [area]を出力します。
期待される出力:
A red shape with area: 78.5 and perimeter: 31.400000000000002
A blue shape with area: 24.0 and perimeter: 20.0
Area: 78.5
Area: 24.0チートシート
抽象メソッドとは、抽象クラス内で宣言され、本体(実装)を持たないメソッドのことです。サブクラスは、独自の実装を提供するために、このメソッドをオーバーライドする必要があります。
abstract class Animal {
// 抽象メソッド - 本体なし
String makeSound();
// 具象メソッド - 本体あり
void breathe() {
print('Breathing...');
}
}サブクラスは、すべての抽象メソッドを実装しなければなりません:
class Dog extends Animal {
@override
String makeSound() {
return 'Woof!';
}
}
class Cat extends Animal {
@override
String makeSound() {
return 'Meow!';
}
}抽象型を変数の型として使用することができます:
Animal a = Dog();
print(a.makeSound()); // Woof!
Animal b = Cat();
print(b.makeSound()); // Meow!| 機能 | 抽象メソッド | 具象メソッド |
|---|---|---|
| 本体の有無 | なし | あり |
| オーバーライドの必須性 | はい | いいえ |
| 目的 | 規約の強制 | 振る舞いの共有 |
| 定義場所 | 抽象クラスのみ | 任意のクラス |
自分で試してみよう
import 'shape.dart';
import 'circle.dart';
import 'rectangle.dart';
void main() {
// TODO: color が 'red' で radius が 5.0 の Circle を作成します
// TODO: color が 'blue'、width が 4.0、height が 6.0 の Rectangle を作成します
// TODO: circle に対して describe() を呼び出します
// TODO: rectangle に対して describe() を呼び出します
// TODO: 両方の図形を含む List<Shape> を作成します
// TODO: リストをループで回し、各図形に対して "Area: [area]" を出力します
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ