デフォルトの方法は何ですか?
Java 8 のリリース後、新しいメソッドをインターフェースに追加できますが、インターフェースはその実装クラスとの互換性を維持できます。開発するライブラリは複数の開発者によって広く使用される可能性があるため、これは重要です。 Java 8 より前では、クラス ライブラリでインターフェイスが公開された後、新しいメソッドがインターフェイスに追加された場合、このインターフェイスを実装したアプリケーションは、新しいバージョンのインターフェイスを使用するとクラッシュする危険がありました。
Java 8 ではそのような危険はありませんか?答えは否定的です。
デフォルトのメソッドをインターフェースに追加すると、特定の実装クラスが使用できなくなる可能性があります。
まず、デフォルトのメソッドの詳細を見てみましょう。
Java 8 では、インターフェイス内のメソッドを実装できます (Java 8 の静的メソッドもインターフェイス内に実装できますが、これは別のトピックです)。インターフェイスに実装されたメソッドはデフォルト メソッドと呼ばれ、修飾子としてキーワード default で識別されます。クラスがインターフェイスを実装する場合、そのインターフェイスにすでに実装されているメソッドを実装できますが、これは必須ではありません。このクラスはデフォルトのメソッドを継承します。これが、インターフェースが変更された場合に実装クラスを変更する必要がない理由です。
多重継承がある場合はどうなるでしょうか?
クラスが複数(2 つなど)のインターフェイスを実装し、これらのインターフェイスが同じデフォルト メソッドを持つ場合、事態は非常に複雑になります。クラスはどのデフォルト メソッドを継承しますか?どちらでもない!この場合、クラス自体 (直接、または継承ツリーの上位クラス) がデフォルト メソッドを実装する必要があります。
あるインターフェースがデフォルトメソッドを実装し、別のインターフェースがデフォルトメソッドを抽象として宣言する場合も同様です。 Java 8 は曖昧さを避け、厳密さを維持しようとします。メソッドが複数のインターフェイスで宣言されている場合、デフォルトの実装は継承されず、コンパイル時エラーが発生します。
ただし、クラスをコンパイルした場合、コンパイル時エラーは発生しません。この時点で、Java 8 には一貫性がありません。これには独自の理由があり、ここでは詳細に説明したり、深く議論したくありません (理由: バージョンがリリースされており、議論の時間が長すぎるため、このプラットフォームにはこれまでにないものがあります)。そのような議論)。
1. 2 つのインターフェイスと 1 つの実装クラスがあるとします。
2. インターフェースの 1 つはデフォルトのメソッド m() を実装します。
3. インターフェースと実装クラスを一緒にコンパイルします。
4. m() メソッドを含まないインターフェイスを変更し、m() メソッドを抽象として宣言します。
5. 変更したインターフェースを個別に再コンパイルします。
6. 実装クラスを実行します。
上記の場合、クラスは正常に実行できます。ただし、変更されたインターフェイスで再コンパイルすることはできませんが、古いインターフェイスでのコンパイルは引き続き実行できます。次に
1. 抽象メソッド m() を含むインターフェースを変更し、デフォルトの実装を作成します。
2. 変更したインターフェースをコンパイルします。
3. クラスを実行します: 失敗しました。
2 つのインターフェイスが同じメソッドのデフォルト実装を提供する場合、実装クラスもデフォルト メソッドを実装しない限り (直接、または継承ツリーの上位レベルのクラスによって)、このメソッドを呼び出すことはできません。
ただし、このクラスは互換性があります。新しいインターフェースを使用してロードでき、両方のインターフェースにデフォルト実装があるメソッドを呼び出さない限り、実行することもできます。
サンプルコード:
上記の例を示すために、C.java のテスト ディレクトリを作成し、その下に I1.java と I2.java を保存する 3 つのサブディレクトリがあります。テスト ディレクトリには、クラス C のソース コード C.java が含まれています。ベース ディレクトリには、コンパイルして実行できるインターフェイスのバージョンが含まれています。 I1 にはデフォルト実装の m() メソッドが含まれていますが、I2 にはメソッドが含まれていません。
実装クラスにはmainメソッドが含まれているので、テストで実行できます。コマンドラインパラメータがあるかどうかをチェックするので、m() を呼び出すテストと 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 インターフェイスが含まれています。
~/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 番目のディレクトリには、I2 インターフェイスも含まれていますm() メソッドは次のように定義されています:
~/github/test$ cat wrong/I2.java public interface I2 { default void m(){ System.out.println("hello interface 2"); } }
我们应该不厌其烦的编译它。尽管m()方法被定义了两次,但是,实现类仍然可以运行,只要它没有调用那个定义了多次的方法,但是,只要我们调用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$
结论
当你把给接口添加了default实现的类库移植到Java 8环境下的时候,一般不会有问题。至少Java8类库开发者给集合类添加default方法的时候就是这么想的。使用你类库的应用程序仍然依赖没有default方法的Java7的类库。当使用和修改多个不同的类库的时候,有很小的几率会发生冲突。如何才能避免呢?
像以前那样设计你的类库。可能依赖default方法的时候不要掉以轻心。万不得已不要使用。明智的选择方法名,避免和其它接口产生冲突。我们将会学习到Java编程中如何使用这个特性做开发。
更多Java8のデフォルトメソッドの詳細な紹介相关文章请关注PHP中文网!