JDK 8のデフォルトは、Javaの多重継承の一形態ですか?


83

JDK 8の新機能により、バイナリ互換性を維持しながら既存のインターフェースに追加できます。

構文は次のようなものです

public interface SomeInterface() {
  void existingInterface();
  void newInterface() default SomeClass.defaultImplementation;
}

このようにしてSomeInterface、この新しいバージョンにアップグレードするときの既存のすべての実装で、突然コンパイルエラーが発生することはありませんnewInterface()

これは素晴らしいことですが、実装しなかった新しいデフォルトのメソッドを両方とも追加した2つのインターフェイスを実装するとどうなりますか?例を挙げて説明しましょう。

public interface Attendance {
   boolean present() default DefaultAttendance.present;
}

public interface Timeline {
   boolean present() default DefaultTimeline.present;
}

public class TimeTravelingStudent implements Attendance, Timeline {

}

// which code gets called?
new TimeTravelingStudent().present();

これはJDK8の一部として定義されていますか?

Javaの神々がここhttp://cs.oswego.edu/pipermail/lambda-lib/2011-February/000068.htmlで似たようなことを話しているのを見つけましたが、それはプライベートメーリングリストの一部であり、直接尋ねることはできません。

JDK 8でデフォルトがどのように使用され、ラムダをサポートするようにコレクションインターフェースを拡張するかについての詳細は、https://oracleus.wingateweb.com/published/oracleus2011/sessions/25066/25066_Cho223662.pdfを 参照してください。


視聴するビデオセッションはこちらですmedianetwork.oracle.com/video/player/1113272518001これは、仮想拡張機能と呼ばれる機能について話している設計者です。彼はまた、これが下位互換性を壊さない方法についても話します。
Peter Lawrey 2011年

したがって、私の例に対する答えは、現在、コンパイラエラーが発生するということです。しかし、それらは他の解決策に開かれています。
パイロリスティック2011年

これに役立つもう1つの資料は、cr.openjdk.java.net
〜briangoetz / lambda /

補足:インターフェイスメソッドのデフォルト実装の最終的な構文は異なることが判明しました(外部クラスへの参照ではなくコードブロック)。
ヨアヒムザウアー2013年

4
Javaには常に型の多重継承があります。デフォルトのメソッドは、動作の多重継承を追加しますが、状態の継承は追加しません。(C ++のような言語での状態の多重継承は、問題のほとんどが発生する場所です。)
Brian Goetz 2014年

回答:


66

重複操作に対する答えは次のとおりです。

多重継承問題を解決するには、同じメソッド名とシグネチャのデフォルト実装を提供する2つのインターフェイスを実装するクラスが、メソッドの実装を提供する必要があります。[記事全文]

あなたの質問に対する私の答えは次のとおりです。はい、異なる親から動作を継承できるため、多重継承の形式です。不足しているのは、状態、つまり属性を継承することです。


2
+1:競合がある場合は、具体性の低い実装よりも具体的な実装が必要になるため、競合が発生した場合にどちらにするかを指定できます。
Peter Lawrey 2011年

@ H-Man2わかりません。それが本当なら、それはバイナリ互換性を壊すことを意味します。おそらくそれは、多重継承を防ぐためにバイナリ互換性を破るほど悪くはなく、バイナリ互換性を破ることは最悪です。
パイロリスティック2011年

@PeterLawrey私の例ではどちらがより具体的ですか?
パイロリスティック2011年

@Pyrolistical:いいえ、コンパイラーはデフォルトの実装を通常のメソッド呼び出しに変換できるため、互換性が損なわれることはありません。実際の継承というよりはマクロだと思いますが、複数の継承の側面をシミュレートできます。たぶん、ピーターからのビデオはより詳細な答えを提供します。
H-Man2 2011年

実際には、抽象ゲッターとセッターを使用することで、欠落している状態を回避できます。実装クラスは、状態を追加できます...
vikkyhacks 2018年

8

私はこれが古い投稿であることを知っています、しかし私がこのようなもので働いているので...

コンパイラからエラーが発生し、次のように通知されます。

クラスTimeTravelingStudentは、present()の無関係なデフォルトをタイプAttendanceから継承します。現在へのタイムライン参照はあいまいです。Timelineのメソッドpresent()と出席のメソッドpresent()の両方が一致します。


7

2つありますシナリオます。

1)最初に、最も具体的なインターフェースないことについて言及しました

public interface A {
   default void doStuff(){ /* implementation */ }
}

public interface B {
   default void doStuff() { /* implementation */ } 
}

public class C implements A, B {
// option 1: own implementation
// OR
// option 2: use new syntax to call specific interface or face compilation error
  void doStuff(){
      B.super.doStuff();
  }
}

2)次に、より具体的なインターフェースがある場合:

   public interface A {
       default void doStuff() { /* implementation */ } 
    }

    public interface B extends A {
       default void doStuff() { /* implementation */ } 
    }

    public class C implements A, B {
    // will use method from B, as it is "closer" to C
    }

4

あなたの質問に対する私の答えは次のとおりです。はい、異なる親から動作を継承できるため、多重継承の形式です。不足しているのは、状態、つまり属性を継承することです。

はい。ただし、実装クラスが実装する必要のあるゲッターとセッターをインターフェイスに追加できます。それにもかかわらず、実装クラスは属性を継承しません。つまり、AFAICSは、多重継承スタイルのソリューションというよりは、トレイトスタイルのソリューションに似ています。


4

つまり、これはコンパイル時のエラーであり、実装でメソッドを手動でオーバーライドする必要があります。


デフォルトメソッドの目的

Java 8にデフォルトのメソッドを導入する主な目的は、既存の実装を壊すことなく、インターフェースを拡張可能にすることです(サードパーティのJavaライブラリーは非常にたくさんあります)。

そしてmultiple inheritance、C ++のように、実際には回避することを目的としていますが、それはJavaのデフォルトメソッドの目的ではありません。


オーバーライドする方法

2つのオプション:

  • 独自のロジックでメソッドをオーバーライドします。
  • メソッドをオーバーライドしsuper、次の形式でインターフェイスのメソッドの1つを呼び出します。<interface_name>.super.<method_name>();

チップ:

  • インターフェイスのメソッドはデフォルトでパブリックになっているためpublic、オーバーライドするときにキーワードを追加することを忘れないでください。

2

誰かがまだ答えを探している場合、クラスが同じデフォルトメソッドで2つのインターフェイスを実装する場合、クラスは独自の実装を提供することによって曖昧さを解決する必要があります。デフォルトメソッドの継承がどのように機能するかの詳細については、このチュートリアルを参照してください。


0

「メソッドをどのように区別するか」は、Stackoverflowに寄せられた質問であり、この質問にJava1.8インターフェイスの具体的なメソッドを参照しました。

以下は、その質問に答える必要がある例です。

interface A{
default public void m(){
System.out.println("Interface A: m()");
}
}

interface B{
default public void m(){
System.out.println("Interface B: m()");
}
}

 class C implements A,B { 

 public void m(){
  System.out.println("Concrete C: m()");   
 }

public static void main(String[] args) {
   C aC = new C();
   aC.m();
   new A(){}.m();
   new B(){}.m();
}
}

上記のクラスCは、インターフェイスAとBの独自の具体的なメソッド実装する必要があります。

 public void m(){
  System.out.println("Interface C: m()");   
 }

呼び出すには、具体的な実装方法から特定のインターフェイスを、次のことができインスタンス化インターフェースをして、明示的に呼び出して、具体的な方法というのインターフェースを

たとえば、次のコードは、インターフェイスAからメソッドm()の具体的な実装を呼び出します

new A(){}.m();

上記の出力は次のようになります。

インターフェイスA:m()


-1

私が見る限り、ステートレスであるため、多重継承ではありません。したがって、仮想拡張メソッドは、オブジェクトまたはクラスの完全な機能をサポートしていません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.