JavaRush /Java Blog /Random-JA /Java 8 のデフォルトのメソッド: 何ができて何ができないのか?
Spitfire
レベル 33

Java 8 のデフォルトのメソッド: 何ができて何ができないのか?

Random-JA グループに公開済み
Peter Verhasによって書かれた2014 年 4 月の記事の翻訳。 Java 8 のデフォルトのメソッド: 何ができて何ができないのか?  - 1翻訳者より: 「デフォルト メソッド」という用語は Java に登場したばかりですが、この用語のロシア語への翻訳が確立されているかどうかはわかりません。理想的ではないと思いますが、「デフォルトの方法」という用語を使用します。より効果的な翻訳について話し合うことをお勧めします。

デフォルトの方法は何ですか

Java 8 のリリースでは、インターフェイスに新しいメソッドを追加して、インターフェイスを実装するクラスとの互換性を維持できるようになりました。キエフからニューヨークまでの多くのプログラマーが使用するライブラリを開発している場合、これは非常に重要です。Java 8 より前では、ライブラリー内にインターフェースを定義した場合、そのインターフェースを実行しているアプリケーションが更新時に中断される危険を冒さずに、そのインターフェースにメソッドを追加することはできませんでした。では、Java 8 では、これを恐れることはもうできないのでしょうか? いいえ、あなたがすることはできません。 デフォルトのメソッドをインターフェースに追加すると、一部のクラスが使用できなくなる可能性があります。 まず、デフォルトのメソッドの良い点を見てみましょう。Java 8 では、メソッドをインターフェイスに直接実装できます。(インターフェース内の静的メソッドも実装できるようになりましたが、それはまた別の話です。)インターフェースに実装されたメソッドはデフォルト メソッドと呼ばれ、default キーワードで示されます。クラスがインターフェイスを実装する場合、インターフェイスに実装されたメソッドを実装できますが、実装する必要はありません。このクラスはデフォルトの実装を継承します。これが、実装するインターフェイスを変更するときにクラスを変更する必要がない理由です。

多重継承?

クラスが複数 (たとえば 2 つ) のインターフェイスを実装し、それらが同じデフォルト メソッドを実装する場合、事態はさらに複雑になります。クラスはどのメソッドを継承しますか? 答えは何もありません。この場合、クラスはメソッド自体を (直接、または別のクラスから継承して) 実装する必要があります。1 つのインターフェイスのみにデフォルト メソッドがあり、もう 1 つのインターフェイスでは同じメソッドが抽象である場合も状況は似ています。Java 8 は規律を保ち、曖昧な状況を回避しようとします。メソッドが複数のインターフェイスで宣言されている場合、デフォルトの実装はクラスによって継承されず、コンパイル エラーが発生します。ただし、クラスがすでにコンパイルされている場合は、コンパイル エラーが発生しない可能性があります。この点に関して、Java 8 は十分に堅牢ではありません。これには理由がありますが、これについては触れたくありません (たとえば、Java リリースはすでにリリースされており、議論の時間はとっくに過ぎており、一般的にここは議論の場ではありません)。
  • 2 つのインターフェイスがあり、クラスがそれらの両方を実装しているとします。
  • インターフェイスの 1 つはデフォルトのメソッド m() を実装します。
  • すべてのインターフェイスとクラスをコンパイルします。
  • m() メソッドを持たないインターフェイスを変更するには、それを抽象メソッドとして宣言します。
  • 変更したインターフェイスのみをコンパイルします。
  • クラスを開始します。
Java 8 のデフォルトのメソッド: 何ができて何ができないのか?  - 2この場合、クラスは機能します。更新されたインターフェイスでコンパイルすることはできませんが、古いバージョンでコンパイルされているため、機能します。今
  • 抽象 m() メソッドを使用してインターフェイスを変更し、デフォルトの実装を追加します。
  • 変更したインターフェースをコンパイルします。
  • クラスの実行: エラー。
メソッドのデフォルト実装を提供する 2 つのインターフェイスがある場合、そのメソッドは、クラス自体によって実装されない限り (やはり、独自に実装されるか、別のクラスから継承されて) クラス内で呼び出すことはできません。 Java 8 のデフォルトのメソッド: 何ができて何ができないのか?  - 3クラス互換性あり。変更されたインターフェイスを使用してロードできます。両方のインターフェイスにデフォルト実装があるメソッドが呼び出されるまで実行される場合もあります。

コード例

Java 8 のデフォルトのメソッド: 何ができて何ができないのか?  - 4上記を実証するために、C.java クラス用のテスト ディレクトリと、ファイル I1.java および I2.java 内のインターフェイス用の 3 つのサブディレクトリを作成しました。テストのルート ディレクトリには、C.java クラスのソース コードが含まれています。ベース ディレクトリには、実行とコンパイルに適したバージョンのインターフェイスが含まれています。インターフェイス I1 にはデフォルトのメソッド m() があります。I2 インターフェイスにはまだメソッドがありません。クラスにはメソッドがあるmainので、それを実行してテストできます。コマンドライン引数があるかどうかをチェックするので、 を呼び出すかどうかに関係なく、簡単に実行できますm()
~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}
コマンドラインからクラスをコンパイルして実行できます。
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
互換ディレクトリには、m() メソッドを抽象メソッドとして宣言する I2 インターフェイスのバージョンと、技術的な理由から、I1.java の未変更のコピーが含まれています。
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
このようなセットを使用して C クラスをコンパイルすることはできません。
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error
エラーメッセージは非常に正確です。ただし、以前のコンパイルからの C.class があり、インターフェイスを互換性のあるディレクトリにコンパイルすると、クラスの実行に引き続き使用できる 2 つのインターフェイスが得られます。
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
3 番目のディレクトリにwrongはバージョン I2 が含まれており、次のメソッドも宣言されていますm()
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
コンパイルについても心配する必要はありません。メソッドが 2 回宣言されていても、m() メソッドが呼び出されるまでクラスは引き続き使用および実行できます。これにはコマンドライン引数が必要です。
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

結論

ライブラリを Java 8 に移植し、インターフェイスを変更してデフォルトのメソッドを含める場合、おそらく問題は発生しません。少なくとも、それが Java 8 ライブラリ開発者が機能を追加する際に期待していることです。ライブラリを使用するアプリケーションは、デフォルトのメソッドがない Java 7 でも引き続きそのライブラリを使用します。複数のライブラリを一緒に使用すると、競合が発生する可能性があります。それを避けるにはどうすればよいでしょうか? 以前と同じ方法でライブラリの API を設計します。デフォルトのメソッドの機能に依存して満足しないでください。それらは最後の手段です。他のインターフェイスとの衝突を避けるために、名前は慎重に選択してください。この機能を使用した Java の開発がどのように発展するかを見てみましょう。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION