1. 序章

Javaインターフェースにdefaultメソッドが導入された後、インターフェースと抽象クラスの間に違いはなくなったように見えました。 しかし、そうではありません—それらの間にはいくつかの根本的な違いがあります。

このチュートリアルでは、インターフェースと抽象クラスの両方を詳しく見て、それらがどのように異なるかを確認します。

2. なぜデフォルトの方法を使用するのですか?

デフォルトメソッドの目的は、既存の実装を壊すことなく外部機能を提供することです。 default メソッドの導入の背後にある元々の動機は、新しいラムダ関数を使用してコレクションフレームワークに下位互換性を提供することでした。

3. デフォルトメソッドと抽象クラスのインターフェイス

主な基本的な違いを見てみましょう。

3.1. 州

abstractクラスは状態を持つことができ、そのメソッドは実装の状態にアクセスできます。 default メソッドはインターフェイスで許可されていますが、実装の状態にアクセスすることはできません。

デフォルトのメソッドで作成するロジックは、インターフェイスの他のメソッドを基準にする必要があります。これらのメソッドは、オブジェクトの状態に依存しません。

CircleClass の状態を表すために、 String colorを含む抽象クラスCircleClassを作成したとします。 ] 物体:

public abstract class CircleClass {

    private String color;
    private List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

    public boolean isValid() {
        if (allowedColors.contains(getColor())) {
            return true;
        } else {
            return false;
        }
    }

    //standard getters and setters
}

上記のabstractクラスには、 CircleClassオブジェクトをその状態に基づいて検証するためのisValid()という非抽象メソッドがあります。 isValid()メソッドは、CircleClassオブジェクトの状態にアクセスし、許可された色に基づいてCircleClassのインスタンスを検証できます。 この動作により、オブジェクトの状態に基づいて抽象クラスメソッドに任意のロジックを記述できます。

CircleClass の簡単な実装クラスを作成しましょう

public class ChildCircleClass extends CircleClass {
}

それでは、インスタンスを作成して色を検証しましょう。

CircleClass redCircle = new ChildCircleClass();
redCircle.setColor("RED");
assertTrue(redCircle.isValid());

ここで、 CircleClass オブジェクトに有効な色を入れて、 isValid()メソッドを内部的に呼び出すと、 isValid()メソッドは次のことができることがわかります。 CircleClass オブジェクトの状態にアクセスし、インスタンスに有効な色が含まれているかどうかを確認します。

defaultメソッドのインターフェイスを使用して同様のことを試してみましょう。

public interface CircleInterface {
    List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

    String getColor();
    
    public default boolean isValid() {
        if (allowedColors.contains(getColor())) {
            return true;
        } else {
            return false;
        }
    }
}

ご存知のように、インターフェースは状態を持つことができないため、defaultメソッドは状態にアクセスできません。

ここでは、状態情報を提供するために getColor()メソッドを定義しました。 子クラスはgetColor()メソッドをオーバーライドして、実行時のインスタンスの状態を提供します。

public class ChidlCircleInterfaceImpl implements CircleInterface {
    private String color;

    @Override
    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

インスタンスを作成し、色を検証してみましょう。

ChidlCircleInterfaceImpl redCircleWithoutState = new ChidlCircleInterfaceImpl();
redCircleWithoutState.setColor("RED");
assertTrue(redCircleWithoutState.isValid());

ここでわかるように、子クラスの getColor()メソッドをオーバーライドして、defaultメソッドが実行時に状態を検証するようにします。

3.2. コンストラクター

抽象クラスはコンストラクターを持つことができ、作成時に状態を初期化できます。 もちろん、インターフェースにはコンストラクターはありません。

3.3. 構文上の違い

さらに、構文に関してはいくつかの違いがあります。 抽象クラスはオブジェクトクラスメソッドをオーバーライドできますが、インターフェイスはオーバーライドできません。

abstractクラスは、可能なすべてのアクセス修飾子を使用してインスタンス変数を宣言でき、子クラスでアクセスできます。 インターフェイスは、 public、 static 、および final 変数のみを持つことができ、インスタンス変数を持つことはできません。

さらに、 abstractクラスはインスタンスと静的ブロックを宣言できますが、インターフェイスはこれらのいずれかを持つことはできません。

最後に、 abstractクラスはラムダ式を参照できませんが、インターフェイスはラムダ式を参照できる単一の抽象メソッドを持つことができます。

4. 結論

この記事では、抽象クラスとdefaultメソッドを使用したインターフェースの違いを示します。 また、シナリオに基づいて、どれが最適かを確認しました。

可能な限り、常にデフォルトのメソッドを使用するインターフェイスを選択する必要があります。これにより、クラスを拡張でき、もインターフェイスを実装できるためです。

いつものように、この記事に示されているすべてのコードサンプルは、GitHubから入手できます。