Menu
Coddy logo textTech

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

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 38/87。

ここまでで abstract クラスとインターフェースの both を理解したところで、どちらを使うべきかという一般的な疑問が生じます。一方を他方より優先して使うべきかは、何をモデル化しようとしているかによって決まります。

共有された state または behavior を持つ共通の基盤を classes が共有する場合は、abstract class を使用します。abstract class には instance variables、constructor、abstract メソッドと concrete メソッドの組み合わせを含めることができます。

public abstract class Animal {
    protected String name;  // 共有状態
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // 共有振る舞い
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // 実装する必要がある
}

無関係なクラス間で共有できる機能を定義したい場合は、インターフェースを使用します。インターフェースは、オブジェクトが何であるかではなく、何ができるかに重点を置きます。

public interface Flyable {
    void fly();
}

// 無関係なクラスがこの機能を共有できる
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }

簡単に比較してみましょう:

機能Abstract Classインターフェース
インスタンス変数はい定数のみ
コンストラクターはいいいえ
多重継承いいえ(単一の extends)はい(複数の implements)
アクセス修飾子任意公開のみ(abstract メソッドの場合)

実践的な指針:abstract メソッドだけを持ち、状態を持たない abstract class を作成していることに気づいたら、インターフェースのほうが適している可能性が高いでしょう。

challenge icon

チャレンジ

簡単

abstract クラスとインターフェースをいつ使うべきかを demonstrates する vehicle system を構築しましょう。abstract クラスを通じて共通の state と behavior を共有する vehicles を model 化し、インターフェースを通じて optional な capability を追加します。

コードを五つのファイルに分けて整理します。

  • Vehicle.java: すべての vehicles の基盤となる abstract class を Create します。すべての vehicle には brand field (String) と year field (int) があります。両方の fields を初期化する constructor、それぞれの getter methods、そして String を返す abstract method startEngine() を含めます。また、[brand] ([year]) を返す concrete method getInfo() も追加します。これは abstract class の perfect な use case です。vehicles は state (brand, year) と一部の behavior (getInfo) を共有しますが、それぞれ異なる方法で engine を start します。
  • Convertible.java: roof を convert できる vehicles のための interface を Declare します。この capability は vehicle が何であるか(is)には tied していません。特定の vehicles がcan doことです。String を返す二つの methods、openRoof()closeRoof() を Declare します。
  • Car.java: Vehicle を extends し、Convertible を implements する class を Create します。Car には追加の numDoors field (int) があります。super を使用して inherited fields を初期化します。startEngine()[brand] car engine started を返すように Implement します。openRoof()[brand] roof opening を返し、closeRoof()[brand] roof closing を返すように Implement します。
  • Motorcycle.java: Vehicle を extends しますが、Convertible は implements しない class を Create します。motorcycles には roofs がありません。Motorcycle には hasSidecar field (boolean) があります。startEngine()[brand] motorcycle engine roaring を返すように Implement します。
  • Main.java: すべてをまとめて、abstract classes と interfaces の違いを showcase します。四つの inputs、つまり car の brand、car の year、motorcycle の brand、motorcycle の year を受け取ります。

    4 doors の Car と、sidecar なしの Motorcycle を Create します。まず、両方の vehicles に対して getInfo() を print し、共有される abstract class の behavior を demonstrate します。これらは両方とも Vehicle からこれを inherit します。次に、それぞれの startEngine() の result を print します。それぞれの vehicle type がこれを異なる方法で Implement していることに注目してください。

    最後に、interface の capability を demonstrate します。Convertible を implements するのは Car だけなので、car に対してのみ openRoof() を call して print し、その後 closeRoof() を call して print します。

四つの inputs、つまり car brand (String)、car year (int)、motorcycle brand (String)、motorcycle year (int) を受け取ります。

出力は合計 six lines になるはずです。両方の vehicles が abstract class の state と getInfo() method を共有しますが、convertible capability を持つのは Car だけであることに注目してください。これは、それぞれの approach をいつ使うべきかを illustrates しています。

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 入力を読み取る
        String carBrand = scanner.nextLine();
        int carYear = scanner.nextInt();
        scanner.nextLine(); // 改行を消費する
        String motorcycleBrand = scanner.nextLine();
        int motorcycleYear = scanner.nextInt();
        
        // TODO: 4ドアのCarを作成する
        
        // TODO: サイドカーなしのMotorcycleを作成する (false)
        
        // TODO: 両方の車両のgetInfo()を出力する(共有された抽象クラスの振る舞いを示す)
        
        // TODO: 両方の車両のstartEngine()を出力する(異なる実装を示す)
        
        // TODO: 車のみのopenRoof()とcloseRoof()を出力する(インターフェースの機能を示す)
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ