Javaのデフォルトキーワードの目的は何ですか?


97

Javaのインターフェースはクラスに似ていますが、インターフェースの本体には抽象メソッドfinalフィールド(定数)のみを含めることができます。

最近、こんな質問がありました

interface AnInterface {
    public default void myMethod() {
        System.out.println("D");
    }
}

インターフェイスの定義によると、抽象メソッドのみが許可されます。上記のコードをコンパイルできるのはなぜですか?defaultキーワードは何ですか?

一方、以下のコードを書き込もうとすると、 modifier default not allowed here

default class MyClass{

}

の代わりに

class MyClass {

}

defaultキーワードの目的を教えてもらえますか?インターフェイス内でのみ許可されていますか?default(アクセス修飾子なし)とどう違うのですか?


4
インターフェイスのデフォルトメソッドはJava8で追加されました。これはアクセス修飾子ではなく、デフォルトの実装です。
エラン2015

2
@Eran、インターフェース定義に違反するデフォルトのメソッドの導入だと思いませんか?:s
Ravi 2015

2
インターフェイスの定義を変更しました。その定義は現在時代遅れです。
Louis Wasserman 2015

2
それらはラムダをサポートするために導入されました。それらが必要な理由の詳細は、ProjectLambdaのストローマン提案にあります。
スプリンター2015

回答:


76

これはJava8の新機能でありinterface、実装を提供できます。Java 8JLS-13.5.6で説明されています(部分的に)読み取るインターフェイスメソッド宣言

defaultメソッドを追加したり、メソッドをからabstractに変更したりしても、default既存のバイナリとの互換性は損なわれませんが、既存のバイナリがIncompatibleClassChangeErrorメソッドを呼び出そうとすると、が発生する可能性があります。修飾型は、このエラーが発生しT、二つのインタフェースのサブタイプであり、IそしてJ、ここで、両方IJ宣言default同じシグネチャおよび結果に方法を、そしてどちらIJ他のサブインターフェイスです。

JDK 8の新機能(一部)

デフォルトのメソッドを使用すると、ライブラリのインターフェイスに新しい機能を追加し、それらのインターフェイスの古いバージョン用に記述されたコードとのバイナリ互換性を確保できます。


16
今では、インターフェイスと抽象クラスはほぼ同じようです。:)
Ravi 2015

16
@jWeaverインターフェースには、コンストラクター、フィールド、プライベートメソッド、またはequals / hashCode / toStringの実装を含めることはできません。
Louis Wasserman 2015

10
@Louis Wasserman:Java 9では、privateメソッドを持つことができます。
ホルガー2015

6
@Dan Pantry:privateメソッドは実際にはインターフェースの一部ではありませんが、default実装のヘルパーメソッドとして、または定数イニシャライザー内で機能する場合があります。インターフェイス内でラムダ式を使用すると、合成privateメソッドが生成されるため、Java8にはすでに存在していることに注意してください。したがって、Java 9では、その機能を非合成、非ラムダの使用にも使用できます…
Holger 2015

14
@jWeaverインターフェースとクラスの違いは、状態動作に還元されます。インターフェイスは動作を実行できますが、状態を持つことができるのはクラスのみです。(フィールド、コンストラクター、equals / hashCodeなどのメソッドは状態に関するものです。)
Brian Goetz 2015

28

デフォルトのメソッドは、主にラムダ式をサポートするためにJava8に追加されました。設計者は(私の見解では巧妙に)、インターフェイスの匿名実装を作成するためのラムダ構文を作成することにしました。ただし、ラムダは単一のメソッドしか実装できないため、単一のメソッドを持つインターフェイスに制限されるため、かなり厳しい制限があります。代わりに、デフォルトのメソッドが追加され、より複雑なインターフェイスを使用できるようになりました。

ラムダdefaultが原因で導入された主張の説得力が必要な場合は、2009年にMarkReinholdによるProjectLambdaのストローマン提案で、ラムダをサポートするために追加する必須機能として「拡張メソッド」が言及されていることに注意してください。

概念を示す例を次に示します。

interface Operator {
    int operate(int n);
    default int inverse(int n) {
        return -operate(n);
    }
}

public int applyInverse(int n, Operator operator) {
    return operator.inverse(n);
}

applyInverse(3, n -> n * n + 7);

非常に工夫されていますがdefault、ラムダをサポートする方法を説明する必要があります。inverseはデフォルトであるため、必要に応じて実装クラスで簡単にオーバーライドできます。


8
これは本当に正しくありません。ラムダが最も近い原因かもしれませんが、実際にはラクダの背中を壊したのはわらでした。本当の動機は、インターフェースの進化を可能にすることでした(既存のインターフェースを互換的に進化させて新しい動作をサポートできるようにする)。ラムダがこの必要性を前面に押し出した要因であった可能性がありますが、機能はそれよりも一般的です。
ブライアンゲッツ2015

@BrianGoetz:IMHO、Javaと.NETの両方は、最初からデフォルトのメソッドが存在していれば、非常に大きなメリットがありました。インターフェイスメンバーのみを使用してインターフェイスの実装に対していくつかの一般的な操作を実行できるが、一部の実装ではそれらをより効率的に実行できる可能性がある場合、インターフェイスはそれらの操作のメソッドを定義し、それらのデフォルトの実装を提供する必要があります。デフォルトの実装を指定できないため、インターフェイスでそのようなメソッドを省略しなければならないというプレッシャーが生じ、後でそれらを追加することができなくなりました。
スーパーキャット2015

@BrianGoetzデフォルトのメソッドにはラムダを超えた重要な価値があることに同意します。しかし、私はあなたがそれらを含める決定を推進するそのより広い価値にあなたが与えることができるどんな参照にも興味があります。私の読書では、ラムダが主な理由でした(これが私の答えで「主に」という単語を使用した理由です)。
スプリンター2015

2
おそらく、このドキュメントが役立つでしょう:cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html。セクション10は、「デフォルトのメソッド(以前は仮想拡張メソッドまたはディフェンダーメソッドと呼ばれていました)の目的は、最初の公開後にインターフェイスを互換性のある方法で進化させることです」と明確に述べています。次に、ラムダに適した方法が、インターフェイスの進化のとして引用されています。
ブライアンゲッツ2015

2
@Kartikあなたは間違った質問をしています!「コンパイラがプログラムを正しく解析するために必要な絶対最小値」に基づいて構文を選択することはありません。「プログラマーの意図を読者にすぐに明らかにするもの」に基づいて選択します。最初にユーザー向けに設計し、次にコンパイラー向けに設計します(ユーザーに関しては、最初に読み取り用に設計し、
BrianGoetz19年

17

他の回答で見落とされていたのは、注釈におけるその役割でした。Java 1.5までさかのぼると、このdefaultキーワードは、注釈フィールドのデフォルト値提供する手段として生まれました。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Processor {
    String value() default "AMD";
}

Java 8の導入により、インターフェースでデフォルトのメソッドを定義できるようになり、使用量が過負荷になりました。

見落とされていた他の何か:宣言default class MyClass {}が無効である理由は、クラスがまったく宣言されている方法によるものです。そのキーワードをそこに表示できるようにする言語の規定はありません。ただし、インターフェイスメソッドの宣言表示されます


16

Java 8には、デフォルトメソッドと呼ばれる新しい概念が導入されています。デフォルトのメソッドは、いくつかのデフォルトの実装があり、既存のコードを壊すことなくインターフェースを進化させるのに役立つメソッドです。例を見てみましょう:

 public interface SimpleInterface {
    public void doSomeWork();

    //A default method in the interface created using "default" keyword

    default public void doSomeOtherWork(){

    System.out.println("DoSomeOtherWork implementation in the interface");
       }
    }

 class SimpleInterfaceImpl implements SimpleInterface{

  @Override
  public void doSomeWork() {
  System.out.println("Do Some Work implementation in the class");
   }

 /*
  * Not required to override to provide an implementation
  * for doSomeOtherWork.
  */

 public static void main(String[] args) {
   SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
   simpObj.doSomeWork();
   simpObj.doSomeOtherWork();
      }
   }

出力は次のとおりです。


インターフェイスのクラスDoSomeOtherWork実装でいくつかの作業の実装を行う


3

新しいJava8機能(デフォルトメソッド)を使用すると、インターフェイスは、defaultキーワードでラベル付けされたときに実装を提供できます。

例えば:

interface Test {
    default double getAvg(int avg) {
        return avg;
    }
}
class Tester implements Test{
 //compiles just fine
}

Interface Testはdefaultキーワードを使用します。これにより、インターフェイスを使用するクラスにこれらのメソッドを実装する必要なしに、インターフェイスがメソッドのデフォルトの実装を提供できるようになります。

下位互換性: インターフェイスが数百のクラスによって実装されていると想像してください。そのインターフェイスを変更すると、インターフェイスを実装する他の多くのクラスには必須ではありませんが、すべてのユーザーが新しく追加されたメソッドを実装する必要があります。

事実と制限:

1-インターフェイス内でのみ宣言でき、クラスまたは抽象クラス内では宣言できません。

2-ボディを提供する必要があります

3-インターフェイスで使用される他の通常のメソッドのように、パブリックまたは抽象であるとは見なされません。


3

インターフェイスのデフォルトのメソッドを使用すると、古いコードを壊すことなく新しい機能を追加できます。

Java 8より前では、新しいメソッドがインターフェースに追加された場合、そのインターフェースのすべての実装クラスは、新しい機能を使用していなくても、その新しいメソッドをオーバーライドするようにバインドされていました。

Java 8ではdefault、メソッド実装の前にキーワードを使用して、新しいメソッドのデフォルト実装を追加できます。

匿名クラスまたは機能インターフェイスを使用している場合でも、一部のコードが再利用可能であり、コード内のどこにでも同じロジックを定義したくない場合は、それらのデフォルトの実装を記述して再利用できます。

public interface YourInterface {
    public void doSomeWork();

    //A default method in the interface created using "default" keyword
    default public void doSomeOtherWork(){

    System.out.println("DoSomeOtherWork implementation in the interface");
       }
    }

    class SimpleInterfaceImpl implements YourInterface{

     /*
     * Not required to override to provide an implementation
     * for doSomeOtherWork.
     */
      @Override
      public void doSomeWork() {
  System.out.println("Do Some Work implementation in the class");
   }

 /*
  * Main method
  */
 public static void main(String[] args) {
   SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
   simpObj.doSomeWork();
   simpObj.doSomeOtherWork();
      }
   }

2

非常に良い説明がJava™チュートリアルにあります。説明の一部は次のとおりです。

車を操作するために呼び出すことができるメソッドを説明する業界標準のインターフェースを公開しているコンピューター制御車のメーカーが関与する例を考えてみましょう。これらのコンピューター制御の自動車メーカーが、フライトなどの新しい機能を自動車に追加した場合はどうなりますか?これらのメーカーは、他の会社(電子誘導機器メーカーなど)が自社のソフトウェアを空飛ぶクルマに適応できるようにするための新しい方法を指定する必要があります。これらの自動車メーカーは、これらの新しい飛行関連の方法をどこで宣言しますか?それらを元のインターフェースに追加する場合、それらのインターフェースを実装したプログラマーは、実装を書き直す必要があります。それらを静的メソッドとして追加する場合、プログラマーはそれらを必須のコアメソッドではなくユーティリティメソッドと見なします。

デフォルトのメソッドを使用すると、ライブラリのインターフェイスに新しい機能を追加し、それらのインターフェイスの古いバージョン用に記述されたコードとのバイナリ互換性を確保できます。


1

デフォルトのメソッドを使用すると、アプリのインターフェースに新しい機能を追加できます。多重継承を持つために使用することもできます。デフォルトのメソッドに加えて、インターフェイスで静的メソッドを定義できます。これにより、ヘルパーメソッドの整理が簡単になります

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