Peter Verhasによって書かれた2014 年 4 月の記事の翻訳。 翻訳者より: 「デフォルト メソッド」という用語は Java に登場したばかりですが、この用語のロシア語への翻訳が確立されているかどうかはわかりません。理想的ではないと思いますが、「デフォルトの方法」という用語を使用します。より効果的な翻訳について話し合うことをお勧めします。
デフォルトの方法は何ですか
Java 8 のリリースでは、インターフェイスに新しいメソッドを追加して、インターフェイスを実装するクラスとの互換性を維持できるようになりました。キエフからニューヨークまでの多くのプログラマーが使用するライブラリを開発している場合、これは非常に重要です。Java 8 より前では、ライブラリー内にインターフェースを定義した場合、そのインターフェースを実行しているアプリケーションが更新時に中断される危険を冒さずに、そのインターフェースにメソッドを追加することはできませんでした。では、Java 8 では、これを恐れることはもうできないのでしょうか? いいえ、あなたがすることはできません。 デフォルトのメソッドをインターフェースに追加すると、一部のクラスが使用できなくなる可能性があります。 まず、デフォルトのメソッドの良い点を見てみましょう。Java 8 では、メソッドをインターフェイスに直接実装できます。(インターフェース内の静的メソッドも実装できるようになりましたが、それはまた別の話です。)インターフェースに実装されたメソッドはデフォルト メソッドと呼ばれ、default キーワードで示されます。クラスがインターフェイスを実装する場合、インターフェイスに実装されたメソッドを実装できますが、実装する必要はありません。このクラスはデフォルトの実装を継承します。これが、実装するインターフェイスを変更するときにクラスを変更する必要がない理由です。多重継承?
クラスが複数 (たとえば 2 つ) のインターフェイスを実装し、それらが同じデフォルト メソッドを実装する場合、事態はさらに複雑になります。クラスはどのメソッドを継承しますか? 答えは何もありません。この場合、クラスはメソッド自体を (直接、または別のクラスから継承して) 実装する必要があります。1 つのインターフェイスのみにデフォルト メソッドがあり、もう 1 つのインターフェイスでは同じメソッドが抽象である場合も状況は似ています。Java 8 は規律を保ち、曖昧な状況を回避しようとします。メソッドが複数のインターフェイスで宣言されている場合、デフォルトの実装はクラスによって継承されず、コンパイル エラーが発生します。ただし、クラスがすでにコンパイルされている場合は、コンパイル エラーが発生しない可能性があります。この点に関して、Java 8 は十分に堅牢ではありません。これには理由がありますが、これについては触れたくありません (たとえば、Java リリースはすでにリリースされており、議論の時間はとっくに過ぎており、一般的にここは議論の場ではありません)。- 2 つのインターフェイスがあり、クラスがそれらの両方を実装しているとします。
- インターフェイスの 1 つはデフォルトのメソッド m() を実装します。
- すべてのインターフェイスとクラスをコンパイルします。
- m() メソッドを持たないインターフェイスを変更するには、それを抽象メソッドとして宣言します。
- 変更したインターフェイスのみをコンパイルします。
- クラスを開始します。
- 抽象 m() メソッドを使用してインターフェイスを変更し、デフォルトの実装を追加します。
- 変更したインターフェースをコンパイルします。
- クラスの実行: エラー。
コード例
上記を実証するために、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$
GO TO FULL VERSION