67

Java では、抽象クラスとインターフェースの間に微妙ではあるが重要な違いがありました:デフォルトの実装です。抽象クラスはそれらを持つことができましたが、インターフェースはできませんでした。ただし、Java 8 ではインターフェースのデフォルト実装が導入されています。つまり、これはもはやインターフェースと抽象クラスの決定的な違いではありません。

それで、何ですか?

私が知る限り、残っている唯一の違い (おそらく内部の効率性の問題を除いて) は、抽象クラスが従来の Java の単一継承に従うのに対し、インターフェイスは複数の継承 (または必要に応じて複数の実装) を持つことができることです。これは私を別の質問に導きます-

新しい Java 8 インターフェースはダイヤモンドの問題をどのように回避しますか?

4

5 に答える 5

69

インターフェースに状態を関連付けることはできません。

抽象クラスには、状態を関連付けることができます。

さらに、インターフェイスのデフォルト メソッドを実装する必要はありません。したがって、この方法では、既存のコードが壊れることはありません。インターフェイスは更新を受け取りますが、実装クラスはそれを実装する必要がないためです。
その結果、最適ではないコードが得られる可能性がありますが、より最適なコードが必要な場合は、デフォルトの実装をオーバーライドする必要があります。

そして最後に、ひし形の問題が発生した場合、コンパイラは警告を表示し、実装するインターフェイスを選択する必要があります。

ひし形の問題について詳しく説明するには、次のコードを検討してください。

interface A {
    void method();
}

interface B extends A {
    @Override
    default void method() {
        System.out.println("B");
    }
}

interface C extends A { 
    @Override
    default void method() {
        System.out.println("C");
    }
}

interface D extends B, C {

}

ここで、次のコンパイラ エラーが発生しますinterface D extends B, C

interface D inherits unrelated defaults for method() form types B and C

修正は次のとおりです。

interface D extends B, C {
    @Override
    default void method() {
        B.super.method();
    }
}

method()fromを継承したい場合B
の場合Dも同様classです。

Java 8 でのインターフェースと抽象クラスの違いについてさらに詳しく説明するには、次のことを考慮してTeamください。

interface Player {

}

interface Team {
    void addPlayer(Player player);
}

addPlayer理論的には、たとえばプレーヤーのリストにプレーヤーを追加できるようなデフォルトの実装を提供できます。
ちょっと待って...?
プレイヤーのリストを保存するにはどうすればよいですか?
答えは、デフォルトの実装が利用可能であっても、インターフェイスでそれを行うことはできないということです。

于 2014-03-23T13:28:32.127 に答える
9

ダイヤモンド問題の定義はあいまいです。多重継承で発生する可能性のあるあらゆる種類の問題があります。幸いなことに、それらのほとんどはコンパイル時に簡単に検出でき、プログラミング言語はこれらの問題を回避するための簡単なソリューションをサポートしています。これらの問題のほとんどは、ダイヤモンドの問題に固有のものではありません。たとえば、メソッドの定義の競合は、ひし形なしでも発生する可能性があります。

interface Bar {
    default int test() { return 42; }
}
interface Baz {
    default int test() { return 6 * 9; }
}
class Foo implements Bar, Baz { }

ダイヤモンドの具体的な問題は、包括的か排他かという問題です。BCがAから派生し、DがBCから派生する型階層がある場合、問題は次のとおりです。

  • D a B *and* a C (つまり、 A の 1 つのタイプ) または
  • D a B *or* a C (つまり、2 種類のA ) です。

さて、Java 8 では、型Ainterfaceでなければなりません。インターフェイスには状態がないため、違いはありません。インターフェイスも状態を持たないため、インターフェイスがデフォルトのメソッドを定義できることは問題ではありません。状態に直接アクセスできるメソッドを呼び出すことができます。ただし、これらのメソッドは常に単一継承に基づいて実装されます。

于 2014-03-23T13:27:41.620 に答える
4

ただし、Java 8 ではインターフェースのデフォルト実装が導入されています。つまり、これはもはやインターフェースと抽象クラスの決定的な違いではありません。

それでも、決定的な違いはほとんどありません。この投稿を参照してください:

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

新しい Java 8 インターフェースはダイヤモンドの問題をどのように回避しますか?

ケース 1: 同じメソッドを持つ 2 つのインターフェイスを実装defaultしています。実装クラスの競合を解決する必要があります。

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}
interface interfaceB{
    default public void foo(){
        System.out.println("InterfaceB foo");
    }
}
public class DiamondExample implements interfaceA,interfaceB{
    public void foo(){
        interfaceA.super.foo();
    }
    public static void main(String args[]){
        new DiamondExample().foo();
    }
} 

上記の例では、以下のアウトアウトが生成されます。

InterfaceA foo

ケース 2: 基本クラスを拡張し、既定のメソッドを使用してインターフェイスを実装しています。コンパイラがひし形の問題を解決してくれるので、最初の例のように解決する必要はありません。

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}

class DiamondBase {
    public void foo(){
        System.out.println("Diamond base foo");
    }
}

public class DiamondExample extends DiamondBase implements interfaceA{

    public static void main(String args[]){
        new DiamondExample().foo();
    }
}

上記の例では、以下の出力が生成されます。

Diamond base foo
于 2016-11-20T02:45:59.910 に答える