ポリモーフィズムと抽象

ポリモーフィズムはOOPのコア機能で、コードをより柔軟で拡張可能にします。

ポリモーフィズムとは

ポリモーフィズムとは、同じメソッドが異なるオブジェクトで異なる動作をすることです。

ポリモーフィズムの利点

利点 説明
柔軟性 同じインターフェース、異なる実装
拡張性 新しいサブクラスを追加する際に親クラスのコードを変更する必要がない
コードの簡素化 親クラスの参照を使用して子オブジェクトを操作

アップキャスト

子オブジェクトを親クラスの参照に代入できます。

JAVA
Animal animal = new Dog();  // アップキャスト

例:アップキャスト

JAVA
public class Animal {
    public void eat() {
        System.out.println("動物が食べている");
    }
}

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("犬は骨をかじっている");
    }
    
    public void bark() {
        System.out.println("吠えている");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        Animal animal = new Dog();  // アップキャスト
        animal.eat();  // 犬は骨をかじっている(Dogのeatを呼び出し)
        // animal.bark();  // コンパイルエラー!親参照は親メソッドにのみアクセス可能
    }
}
▶ 試してみよう

動的バインディング

実行時に、コンパイル時の型ではなく実際のオブジェクトの型に基づいてメソッドが呼び出されます。

例:動的バインディング

JAVA
public class Animal {
    public void speak() {
        System.out.println("動物が声を出している");
    }
}

public class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("ワンワン");
    }
}

public class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("ニャーニャー");
    }
}

public class DynamicBinding {
    public static void makeSound(Animal animal) {
        animal.speak();  // 実行時にどのspeakを呼び出すか決定
    }
    
    public static void main(String[] args) {
        makeSound(new Dog());  // ワンワン
        makeSound(new Cat());  // ニャーニャー
    }
}
▶ 試してみよう

抽象クラス

抽象クラスはインスタンス化できず、継承されることのみ可能です。

抽象メソッド

サブクラスが実装する必要がある、本体のないメソッド。

JAVA
public abstract class Shape {
    // 抽象メソッド:本体なし
    public abstract double area();
    
    // 通常のメソッド:本体あり
    public void display() {
        System.out.println("面積: " + area());
    }
}

例:抽象クラス

JAVA
public abstract class Shape {
    protected String name;
    
    public Shape(String name) {
        this.name = name;
    }
    
    // 抽象メソッド
    public abstract double area();
    public abstract double perimeter();
    
    public void display() {
        System.out.println(name + " - 面積: " + area() + ", 周長: " + perimeter());
    }
}

public class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        super("円");
        this.radius = radius;
    }
    
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

public class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(double width, double height) {
        super("長方形");
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double area() {
        return width * height;
    }
    
    @Override
    public double perimeter() {
        return 2 * (width + height);
    }
}

public class ShapeDemo {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rect = new Rectangle(4, 6);
        
        circle.display();  // 円 - 面積: 78.54, 周長: 31.42
        rect.display();    // 長方形 - 面積: 24.0, 周長: 20.0
    }
}
▶ 試してみよう

インターフェース

インターフェースは、実装クラスが従うべきルールのセットを定義します。

インターフェースの定義

JAVA
public interface インターフェース名 {
    // 定数(デフォルトでpublic static final)
    int MAX_SIZE = 100;
    
    // 抽象メソッド(デフォルトでpublic abstract)
    void method1();
    int method2(int a);
}

インターフェースの実装

JAVA
public class クラス名 implements インターフェース名 {
    @Override
    public void method1() {
        // 実装
    }
    
    @Override
    public int method2(int a) {
        return a;
    }
}

例:インターフェース

JAVA
public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("アヒルが飛んでいる");
    }
    
    @Override
    public void swim() {
        System.out.println("アヒルが泳いでいる");
    }
}

public class InterfaceDemo {
    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.fly();   // アヒルが飛んでいる
        duck.swim();  // アヒルが泳いでいる
        
        // インターフェース参照
        Flyable flyer = new Duck();
        flyer.fly();
    }
}
▶ 試してみよう

インターフェース vs 抽象クラス

特徴 インターフェース 抽象クラス
キーワード interface abstract class
実装 implements extends
複数実装 サポート サポートなし
コンストラクタ なし コンストラクタあり
メンバー変数 定数のみ 通常の変数を持てる
メソッド 抽象メソッド(Java 8以前) 通常のメソッドを持てる

インターフェース指向プログラミング

JAVA
public interface Payment {
    void pay(double amount);
}

public class Alipay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("Alipay支払い: $" + amount);
    }
}

public class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("WeChat支払い: $" + amount);
    }
}

public class PaymentDemo {
    // インターフェース指向
    public static void checkout(Payment payment, double amount) {
        payment.pay(amount);
    }
    
    public static void main(String[] args) {
        checkout(new Alipay(), 100);      // Alipay支払い: $100.0
        checkout(new WechatPay(), 200);   // WeChat支払い: $200.0
    }
}

❓ よくある質問

Q 抽象クラスとインターフェースのどちらを使うべきですか?
A 共有状態と動作がある場合は抽象クラスを使用し、契約を定義するだけの場合はインターフェースを使用します。
Q インターフェースにメソッド本体を持たせることはできますか?
A はい、Java 8以降、defaultキーワードを使用してデフォルトメソッドを定義できます。
Q アップキャスト後に子固有のメソッドを呼び出すことはできますか?
A いいえ、最初にダウンキャスト(明示的キャスト)を行う必要があります。

📖 まとめ

📝 演習

  1. Animal階層: 抽象AnimalクラスとDog、Catサブクラスを定義し、ポリモーフィック呼び出しを実装
  2. Shape計算: Shapeインターフェースを定義し、Circle、Rectangleを実装し、面積を計算
  3. 決済システム: Paymentインターフェースを定義し、複数の決済方法を実装

次のレッスン

次のレッスンでは、インターフェースの応用とLambdaを学びます — インターフェースの高度な機能とLambda式。

100%