1概要

Java 8は、https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html[ラムダ式]を含む、いくつかのまったく新しい機能を表にまとめたものです。機能インターフェース]、リンク:/java-8-ダブルコロン演算子[メソッド参照]、リンク:/java-8-streams[ストリーム]、リンク:/java-optional[オプション]、およびインターフェースの

static

メソッドと

default

メソッド。

それらのうちのいくつかはすでに

この記事

。それにもかかわらず、インタフェースの

static



default

メソッドはそれ自体でもっと深く見る価値があります。

この記事では、インターフェースで

static

メソッドと

default

メソッドを使用する方法** について詳しく説明し、それらが役立つ可能性があるいくつかのユースケースについて説明します。


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

通常のインターフェースメソッドと同じように、

デフォルトメソッドは暗黙のうちにpublic

です。

通常のインターフェースメソッドとは異なり、これらはメソッドのシグネチャの先頭に

default

キーワードを使用して宣言され、実装を提供します。

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

public interface MyInterface {

   //regular interface methods

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


default

メソッドがJava 8リリースに含まれた理由はかなり明白です。

抽象化に基づく典型的な設計では、インタフェースが1つまたは複数の実装を持ち、1つ以上のメソッドがインタフェースに追加されると、すべての実装はそれらも実装することを余儀なくされます。

そうでなければ、デザインは壊れてしまいます。

デフォルトのインターフェースメソッドは、この問題に対処するための効率的な方法です。

それらは、実装で自動的に利用可能な新しいメソッドをインタフェースに追加することを可能にします。したがって、実装クラスを変更する必要はありません。

このようにして、実装者をリファクタリングする必要なしに、後方互換性がきれいに維持されます。

** 3動作中のデフォルトインタフェースメソッド

**


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.";
    }
}

最後に、

Car

のインスタンスを作成し、そのメソッドを呼び出す典型的な

main

クラスを定義しましょう。

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()



Car

クラスで** 自動的に使用可能になっていることに注意してください。

さらに、ある時点で

Vehicle

インターフェースにさらに

default

メソッドを追加することにした場合でも、アプリケーションは引き続き機能し続けるため、クラスに新しいメソッドの実装を強制する必要はありません。

インターフェースでのデフォルトメソッドの最も一般的な使い方は、実装クラスを壊すことなく、与えられた型に追加機能を追加的に提供することです。

さらに、これらは既存の抽象メソッドの周りに追加の機能を提供するためにも使用できます。

public interface Vehicle {

   //additional interface methods

    double getSpeed();

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


4多重インターフェース継承規則

デフォルトのインターフェースメソッドは確かに非常に優れた機能ですが、いくつかの注意点があります。 Javaはクラスが複数のインターフェースを実装することを可能にするので、クラスが同じ

default

メソッドを定義する複数のインターフェースを実装するとき** 何が起こるかを知ることは重要です。

このシナリオを理解するために、新しい

Alarm

インターフェイスを定義し、

Car

クラスをリファクタリングしましょう。

public interface Alarm {

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

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

この新しいインタフェースが独自の

default

メソッドのセットを定義すると、

Car

クラスは

Vehicle



Alarm

の両方を実装します。

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

この場合、

複数のインターフェースの継承によって競合が発生するため、

コードは単純にコンパイルされません**


Car

クラスは両方の

default

メソッドを継承します。そのときどれを呼ぶべきですか?

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

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

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

  • 私たちのクラスに一つのインタフェースの

    default

    メソッドを使わせることもできます。


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();
}

さらに、

Car

クラスで両方のデフォルトメソッドを使用することも可能です。

@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では、インタフェースで

static

メソッドを定義して実装することができます。

静的メソッドは特定のオブジェクトに属していないため、インターフェースを実装するクラスのAPIの一部ではなく、メソッド名の前にインターフェース名を使用して呼び出す必要があります。


static

メソッドがインターフェイスでどのように機能するかを理解するために、

Vehicle

インターフェイスをリファクタリングし、それに

static

ユーティリティメソッドを追加しましょう。

public interface Vehicle {

   //regular/default interface methods

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

  • インターフェイス内で

    static

    メソッドを定義することは、クラス内で定義することと同じです。

それでは、与えられた車両のエンジンのhttps://en.wikipedia.org/wiki/Horsepower[horsepower]を計算したいとしましょう。

getHorsePower()

メソッドを呼び出すだけです。

Vehicle.getHorsePower(2500, 480));


static

インターフェースメソッドの背後にある考え方は、関連するメソッドをまとめてhttps://en.wikipedia.org/wiki/Cohesion

(computer

science)[cohesion]** の程度を高めることができるシンプルなメカニズムを提供することです。オブジェクトを作成せずに1か所

抽象クラスでもほとんど同じことが可能です** 主な違いは、抽象クラスはコンストラクタ、状態、および振る舞いを持つことができるという点です。

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


6. 結論

この記事では、Java 8での

static

および

default

インターフェース・メソッドの使用法について詳しく説明しました。一見したところ、特にオブジェクト指向の純粋主義者の観点からは、この機能は少しずさんに見えるかもしれません。理想的には、インターフェースは振る舞いをカプセル化するのではなく、特定のタイプのパブリックAPIを定義するためだけに使用されるべきです。

ただし、既存のコードとの下位互換性を維持するためには、

static

および

default

メソッドを使用することをお勧めします。

そして、いつもどおり、この記事に示されているすべてのコード・サンプルはhttps://github.com/eugenp/tutorials/tree/master/core-java-8[GitHubで利用可能]から入手できます。