Javaのインタフェースにおける静的メソッドとデフォルトメソッド

Javaのインターフェースの静的メソッドとデフォルトメソッド

1. 概要

Java 8は、lambda expressionsfunctional interfacesmethod referencesstreamsOptionalstaticなどのいくつかの新しい機能をテーブルにもたらしました。インターフェイスのdefaultメソッド。

それらのいくつかはすでにthis articleでカバーされています。 それにもかかわらず、インターフェイスのstaticおよびdefaultメソッドは、それ自体でより深く調べる価値があります。

この記事では、how to use static and default methods in interfacesについて詳しく説明し、それらが役立つ可能性のあるいくつかのユースケースについて説明します。

2. インターフェイスにデフォルトのメソッドが必要な理由

通常のインターフェースメソッドと同様に、default methods are implicitly publicpublic修飾子を指定する必要はありません。

通常のインターフェースメソッドとは異なり、それらはdeclared with the default keyword at the beginning of the method signatureであり、provide an implementationです。

簡単な例を見てみましょう。

public interface MyInterface {

    // regular interface methods

    default void defaultMethod() {
        // default method implementation
    }
}

defaultメソッドがJava8リリースに含まれている理由は非常に明白です。

インターフェースに1つまたは複数の実装がある抽象化に基づく典型的な設計では、1つまたは複数のメソッドがインターフェースに追加されると、すべての実装もそれらの実装を強制されます。 そうしないと、デザインが壊れてしまいます。

デフォルトのインターフェースメソッドは、この問題に対処する効率的な方法です。 それらはallow us to add new methods to an interface that are automatically available in the implementationsです。 したがって、実装クラスを変更する必要はありません。

このようにして、実装者をリファクタリングすることなく、backward compatibility is neatly preservedを実行できます。

3. デフォルトのInterface Methods in Action

defaultインターフェースメソッドの機能をよりよく理解するために、簡単な例を作成しましょう。

単純なVehicleインターフェースと1つの実装だけがあるとしましょう。 もっとあるかもしれませんが、それを単純に保ちましょう。

public interface Vehicle {

    String getBrand();

    String speedUp();

    String slowDown();

    default String turnAlarmOn() {
        return "Turning the vehicle alarm on.";
    }

    default String turnAlarmOff() {
        return "Turning the vehicle alarm off.";
    }
}

そして、実装クラスを書いてみましょう。

public class Car implements Vehicle {

    private String brand;

    // constructors/getters

    @Override
    public String getBrand() {
        return brand;
    }

    @Override
    public String speedUp() {
        return "The car is speeding up.";
    }

    @Override
    public String slowDown() {
        return "The car is slowing down.";
    }
}

最後に、典型的なmainクラスを定義しましょう。これは、Carのインスタンスを作成し、そのメソッドを呼び出します。

public static void main(String[] args) {
    Vehicle car = new Car("BMW");
    System.out.println(car.getBrand());
    System.out.println(car.speedUp());
    System.out.println(car.slowDown());
    System.out.println(car.turnAlarmOn());
    System.out.println(car.turnAlarmOff());
}

VehicleインターフェースのdefaultメソッドturnAlarmOn()およびturnAlarmOff()automatically available in the Car classであることに注意してください。

さらに、ある時点でdefaultメソッドをVehicleインターフェイスに追加することにした場合でも、アプリケーションは引き続き機能し、クラスに新しいメソッドの実装を提供するように強制する必要はありません。 。

インターフェイスでのデフォルトメソッドの最も一般的な使用法はto incrementally provide additional functionality to a given type without breaking down the implementing classes.です。

さらに、それらはprovide additional functionality around an existing abstract methodに使用できます。

public interface Vehicle {

    // additional interface methods

    double getSpeed();

    default double getSpeedInKMH(double speed) {
       // conversion
    }
}

4. 複数のインターフェース継承ルール

デフォルトのインターフェースメソッドは確かに素晴らしい機能ですが、言及する価値があるいくつかの注意事項があります。 Javaではクラスが複数のインターフェースを実装できるため、what happens when a class implements several interfaces that define the same default methodsを知っておくことが重要です。

このシナリオをよりよく理解するために、新しいAlarmインターフェースを定義し、Carクラスをリファクタリングしましょう。

public interface Alarm {

    default String turnAlarmOn() {
        return "Turning the alarm on.";
    }

    default String turnAlarmOff() {
        return "Turning the alarm off.";
    }
}

この新しいインターフェイスが独自のdefaultメソッドのセットを定義すると、CarクラスはVehicleAlarmの両方を実装します。

public class Car implements Vehicle, Alarm {
    // ...
}

この場合、the code simply won’t compile, as there’s a conflict caused by multiple interface inheritance(別名Diamond Problem)。 Carクラスは、defaultメソッドの両方のセットを継承します。 そのとき、どれを呼び出すべきですか?

このあいまいさを解決するには、メソッドの実装を明示的に提供する必要があります。

@Override
public String turnAlarmOn() {
    // custom implementation
}

@Override
public String turnAlarmOff() {
    // custom implementation
}

have our class use the default methods of one of the interfacesも可能です。

Vehicleインターフェースからdefaultメソッドを使用する例を見てみましょう。

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff();
}

同様に、Alarmインターフェイス内で定義されたdefaultメソッドをクラスに使用させることができます。

@Override
public String turnAlarmOn() {
    return Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Alarm.super.turnAlarmOff();
}

さらに、それはpossible to make the Car class use both sets of default methodsですらあります。

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}

5. 静的インターフェースメソッド

インターフェイスでdefaultメソッドを宣言できることは別として、Java 8 allows us to define and implement static methods in interfaces

staticメソッドは特定のオブジェクトに属していないため、インターフェースを実装するクラスのAPIの一部ではなく、called by using the interface name preceding the method nameである必要があります。

staticメソッドがインターフェイスでどのように機能するかを理解するために、Vehicleインターフェイスをリファクタリングし、それにstaticユーティリティメソッドを追加しましょう。

public interface Vehicle {

    // regular / default interface methods

    static int getHorsePower(int rpm, int torque) {
        return (rpm * torque) / 5252;
    }
}

Defining a static method within an interface is identical to defining one in a class.さらに、staticメソッドは、他のstaticおよびdefaultメソッド内で呼び出すことができます。

ここで、特定の車両のエンジンのhorsepowerを計算するとします。 getHorsePower()メソッドを呼び出すだけです。

Vehicle.getHorsePower(2500, 480));

staticインターフェイスメソッドの背後にある考え方は、オブジェクトを作成せずに関連するメソッドを1つの場所にまとめることで、デザインのincrease the degree of cohesionを可能にする単純なメカニズムを提供することです。

ほぼthe same can be done with abstract classes.主な違いは、abstract classes can have constructors, state, and behaviorという事実にあります。

さらに、インターフェイスの静的メソッドを使用すると、静的メソッドの単なるプレースホルダーである人工的なユーティリティクラスを作成することなく、関連するユーティリティメソッドをグループ化できます。

6. 結論

この記事では、Java 8でのstaticおよびdefaultインターフェイスメソッドの使用について詳しく説明しました。 一見したところ、この機能は、特にオブジェクト指向の純粋主義の観点からは、少しずさんに見えるかもしれません。 理想的には、インターフェースは動作をカプセル化するべきではなく、特定のタイプのパブリックAPIを定義するためにのみ使用する必要があります。

ただし、既存のコードとの下位互換性を維持することになると、staticメソッドとdefaultメソッドは適切なトレードオフになります。

そして、いつものように、この記事に示されているすべてのコードサンプルは利用可能なover on GitHubです。