web-dev-qa-db-ja.com

Java 8の同じ署名の2つのデフォルトメソッドで2つのインターフェイスを実装

2つのインターフェイスがあるとします。

public interface I1
{
    default String getGreeting() {
        return "Good Morning!";
    }
}

public interface I2
{
    default String getGreeting() {
        return "Good Afternoon!";
    }
}

両方を実装する場合、どの実装が使用されますか?

public class C1 implements I1, I2
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }
}
41
kavai77

これはコンパイル時エラーです。 2つのインターフェイスから2つの実装を持つことはできません。

ただし、C1getGreetingメソッドを実装する場合は正しいです。

public class C1 implements I1, I2 // this will compile, bacause we have overridden getGreeting()
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }

    @Override public String getGreeting()
    {
        return "Good Evening!";
    }
}

I1のメソッドが抽象的で、I2のデフォルトである場合でも、両方を実装することはできません。したがって、これもコンパイル時エラーです。

public interface I1
{
    String getGreeting();
}

public interface I2
{
    default String getGreeting() {
        return "Good afternoon!";
    }
}

public class C1 implements I1, I2 // won't compile
{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }
}
57
kavai77

これは質問に固有のものではありません。しかし、それでもコンテキストに価値を加えると思います。 @ toni77の答えに加えて、以下に示すように、デフォルトのメソッドを実装クラスから呼び出すことができることを追加したいと思います。以下のコードでは、デフォルトのメソッドgetGreeting() from interface I1は、オーバーライドされたメソッドから呼び出されます。

public interface I1 {
     default String getGreeting() {
        return "Good Morning!";
     }
}

public class C1 implements I1, I2 {       
    @override
    public String getGreeting() {
        return I1.super.getGreeting();
    }
}
23
Keerthivasan

クラスに2つのインターフェイスが実装され、両方に同じシグネチャを持つJava-8デフォルトメソッドがある場合(例のように)、実装クラスは メソッドをオーバーライドする必要があります です。クラス まだデフォルトのメソッドにアクセスできますI1.super.getGreeting();を使用して。いずれか、両方、またはどちらにもアクセスできません。したがって、次はC1の有効な実装になります。

public class C1 implements I1, I2{
    public static void main(String[] args)
    {
        System.out.println(new C1().getGreeting());
    }

    @Override //class is obliged to override this method
    public String getGreeting() {
        //can use both default methods
        return I1.super.getGreeting()+I2.super.getGreeting();
    }

    public String useOne() {
        //can use the default method within annother method
        return "One "+I1.super.getGreeting();
    }

    public String useTheOther() {
        //can use the default method within annother method
        return "Two "+I2.super.getGreeting();
    }


}
9
Richard Tingle

これが実際に解決規則に従って機能する場合があります。インターフェイスの1つが他の1つを拡張する場合。

上記の例を使用します。

public interface I2 extends I1 {
    default String getGreeting() {
        return "Good Afternoon!";
    }
}

結果は次のようになります。

こんにちは!

しかし、これは大きな問題になると思います。デフォルトのインターフェースの全体的な理由は、ライブラリ開発者が実装者を壊さずにAPIを進化させることです。

ライブラリ開発者が潜在的に動作をハイジャックする可能性があるため、拡張機能を介した継承構造なしでメソッドをコンパイルすることはできません。

ただし、これには自己破綻の可能性があります。クラスが階層ビューからは関係のない2つのインターフェイスを実装しているが、両方が同じデフォルトのメソッドシグネチャを定義している場合、両方のインターフェイスを拡張するクラスはコンパイルされません。 (上記のように)

2人の異なるライブラリ開発者が、共通の署名を使用して異なる時点でデフォルトのメソッドを追加することを決定できると考えられます。実際、数学ライブラリなどの同様の概念を実装するライブラリでこれが発生する可能性があります。同じクラスで両方のインターフェイスを実装することを残念な魂にした場合、更新時に壊れます。

2
TechTrip

ルールは、重複するデフォルトメソッドを実装するクラスが実装を「必ず」オーバーライドすることだと思います。以下はコンパイルして正常に実行されます...

public class DupeDefaultInterfaceMethods {

interface FirstAbility {
    public default boolean doSomething() {
        return true;
    }
}

interface SecondAbility {
    public default boolean doSomething() {
        return true;
    }
}

class Dupe implements FirstAbility, SecondAbility {
    @Override
    public boolean doSomething() {
        return false;
    }
}

public static void main(String[] args) {
    DupeDefaultInterfaceMethods ddif = new DupeDefaultInterfaceMethods();
    Dupe dupe = ddif.new Dupe();
    System.out.println(dupe.doSomething());

    }
}

> false
0
Eddie B