Day 7: 継承とポリモーフィズム
今日学ぶこと
- 継承(extends)
- メソッドのオーバーライド
- 抽象クラス
- インターフェース
- ポリモーフィズム
- sealed クラス(Java 17+)
継承
既存のクラスを拡張して新しいクラスを作る仕組みです。
// 親クラス(スーパークラス)
public class Animal {
protected String name;
Animal(String name) {
this.name = name;
}
void speak() {
System.out.println(name + ": ...");
}
}
// 子クラス(サブクラス)
public class Dog extends Animal {
private String breed;
Dog(String name, String breed) {
super(name); // 親のコンストラクタを呼ぶ
this.breed = breed;
}
@Override
void speak() {
System.out.println(name + ": ワン!");
}
void fetch() {
System.out.println(name + "がボールを取ってきた!");
}
}
flowchart TB
Animal["Animal<br>name, speak()"]
Dog["Dog<br>breed, fetch()"]
Cat["Cat<br>indoor"]
Animal --> Dog
Animal --> Cat
style Animal fill:#3b82f6,color:#fff
style Dog fill:#22c55e,color:#fff
style Cat fill:#22c55e,color:#fff
| 用語 | 説明 |
|---|---|
extends |
クラスの継承 |
super |
親クラスへの参照 |
@Override |
メソッドの上書き |
protected |
サブクラスからアクセス可能 |
制約: Javaは単一継承です。1つのクラスしか
extendsできません。
メソッドのオーバーライド
public class Cat extends Animal {
Cat(String name) {
super(name);
}
@Override
void speak() {
System.out.println(name + ": ニャー!");
}
}
// 使用
Animal dog = new Dog("ポチ", "柴犬");
Animal cat = new Cat("ミケ");
dog.speak(); // ポチ: ワン!
cat.speak(); // ミケ: ニャー!
オーバーライドのルール
| ルール | 説明 |
|---|---|
| メソッド名 | 同じ |
| 引数 | 同じ |
| 戻り値 | 同じまたはサブタイプ |
| アクセス修飾子 | 同じまたはより広く |
@Override |
付けることを推奨 |
抽象クラス
インスタンス化できないクラスです。サブクラスに実装を強制します。
abstract class Shape {
String color;
Shape(String color) {
this.color = color;
}
// 抽象メソッド(実装なし)
abstract double area();
abstract double perimeter();
// 具象メソッド(実装あり)
void display() {
System.out.printf("%s: 面積=%.2f, 周長=%.2f%n",
color, area(), perimeter());
}
}
class Circle extends Shape {
double radius;
Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
double area() {
return Math.PI * radius * radius;
}
@Override
double perimeter() {
return 2 * Math.PI * radius;
}
}
class Rectangle extends Shape {
double width, height;
Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
double area() {
return width * height;
}
@Override
double perimeter() {
return 2 * (width + height);
}
}
使い分け: 抽象クラスは**共通の状態(フィールド)**を持つ場合に使います。
インターフェース
クラスが実装すべき契約を定義します。
interface Printable {
void print(); // 抽象メソッド
}
interface Savable {
void save(String filename);
// デフォルトメソッド(Java 8+)
default void autoSave() {
save("auto_save.dat");
}
}
// 複数のインターフェースを実装
class Document implements Printable, Savable {
private String content;
Document(String content) {
this.content = content;
}
@Override
public void print() {
System.out.println("印刷: " + content);
}
@Override
public void save(String filename) {
System.out.println(filename + " に保存: " + content);
}
}
flowchart TB
Printable["«interface»<br>Printable<br>print()"]
Savable["«interface»<br>Savable<br>save(), autoSave()"]
Document["Document<br>content"]
Printable --> Document
Savable --> Document
style Printable fill:#8b5cf6,color:#fff
style Savable fill:#8b5cf6,color:#fff
style Document fill:#22c55e,color:#fff
抽象クラス vs インターフェース
| 特徴 | 抽象クラス | インターフェース |
|---|---|---|
| 継承 | 1つだけ | 複数可能 |
| フィールド | あり | 定数のみ |
| コンストラクタ | あり | なし |
| デフォルトメソッド | あり | あり(Java 8+) |
| 用途 | 共通の状態と振る舞い | 契約の定義 |
ポリモーフィズム
同じ型の変数で異なる実装を呼び出せる仕組みです。
Shape[] shapes = {
new Circle("赤", 5),
new Rectangle("青", 4, 6),
new Circle("緑", 3)
};
for (Shape shape : shapes) {
shape.display(); // 各サブクラスのメソッドが呼ばれる
}
instanceof とパターンマッチング(Java 16+)
// 従来の書き方
if (shape instanceof Circle) {
Circle c = (Circle) shape;
System.out.println("半径: " + c.radius);
}
// パターンマッチング(Java 16+)
if (shape instanceof Circle c) {
System.out.println("半径: " + c.radius);
}
// switch式でのパターンマッチング(Java 21+)
String info = switch (shape) {
case Circle c -> "円(半径: " + c.radius + ")";
case Rectangle r -> "長方形(" + r.width + " × " + r.height + ")";
default -> "不明な図形";
};
sealed クラス(Java 17+)
継承できるクラスを制限します。
sealed abstract class Payment permits CreditCard, BankTransfer, Cash {
abstract double amount();
}
final class CreditCard extends Payment {
private double amount;
private String cardNumber;
CreditCard(double amount, String cardNumber) {
this.amount = amount;
this.cardNumber = cardNumber;
}
@Override
double amount() { return amount; }
}
final class BankTransfer extends Payment {
private double amount;
BankTransfer(double amount) {
this.amount = amount;
}
@Override
double amount() { return amount; }
}
final class Cash extends Payment {
private double amount;
Cash(double amount) {
this.amount = amount;
}
@Override
double amount() { return amount; }
}
| 修飾子 | 説明 |
|---|---|
sealed |
許可されたサブクラスのみ継承可能 |
permits |
継承を許可するクラスの一覧 |
final |
これ以上継承不可 |
non-sealed |
制限を解除 |
実践: 動物園シミュレーション
interface Feedable {
String favoriteFood();
}
abstract class Animal {
protected String name;
protected int age;
Animal(String name, int age) {
this.name = name;
this.age = age;
}
abstract String speak();
@Override
public String toString() {
return String.format("%s(%d歳)", name, age);
}
}
class Dog extends Animal implements Feedable {
Dog(String name, int age) { super(name, age); }
@Override
String speak() { return "ワン!"; }
@Override
public String favoriteFood() { return "骨"; }
}
class Cat extends Animal implements Feedable {
Cat(String name, int age) { super(name, age); }
@Override
String speak() { return "ニャー!"; }
@Override
public String favoriteFood() { return "魚"; }
}
class Parrot extends Animal implements Feedable {
private String phrase;
Parrot(String name, int age, String phrase) {
super(name, age);
this.phrase = phrase;
}
@Override
String speak() { return phrase; }
@Override
public String favoriteFood() { return "種"; }
}
public class Zoo {
public static void main(String[] args) {
Animal[] animals = {
new Dog("ポチ", 3),
new Cat("ミケ", 5),
new Parrot("ピーちゃん", 2, "こんにちは!")
};
System.out.println("=== 動物園 ===");
for (Animal animal : animals) {
System.out.printf("%s → %s", animal, animal.speak());
if (animal instanceof Feedable f) {
System.out.printf(" [好物: %s]", f.favoriteFood());
}
System.out.println();
}
}
}
まとめ
| 概念 | 説明 |
|---|---|
| 継承 | extendsで既存クラスを拡張 |
super |
親クラスのコンストラクタ・メソッド呼び出し |
@Override |
メソッドの上書き |
| 抽象クラス | インスタンス化不可、サブクラスに実装を強制 |
| インターフェース | 複数実装可能な契約 |
| ポリモーフィズム | 同じ型で異なる振る舞い |
sealed |
継承先を制限(Java 17+) |
| パターンマッチング | instanceofの簡潔な記述(Java 16+) |
重要ポイント
- Javaは単一継承(
extendsは1つだけ) - インターフェースは複数実装可能
- ポリモーフィズムでコードの柔軟性を高める
- **
sealed**で継承を安全に制限
練習問題
問題1: 基本
Vehicleクラスを親として、CarとBicycleを作成し、それぞれのtoString()をオーバーライドしてください。
問題2: 応用
Comparableインターフェースを実装したStudentクラスを作成し、成績でソートできるようにしてください。
チャレンジ問題
sealedクラスを使って、図形の面積計算をパターンマッチング(switch式)で実装してください。Circle, Rectangle, Triangleをサポートし、それぞれの面積を返しましょう。
参考リンク
次回予告: Day 8では「例外処理とファイルI/O」について学びます。エラーハンドリングの仕組みをマスターしましょう。