一見すると、デフォルトのメソッドは Java 仮想マシンの命令セットに多くの新機能をもたらします。最終的に、ライブラリ開発者は、クライアント コードとの互換性の問題を引き起こすことなく API をアップグレードできるようになります。デフォルト メソッドを使用すると、ライブラリ インターフェイスを実装するクラスは、そのインターフェイスによって導入されたデフォルト メソッドに自動的に適応します。ユーザーが実装したクラスを更新すると、元のデフォルト メソッドをより意味のあるメソッドで簡単にオーバーライドできます。さらに良いことに、ユーザーはメソッドをオーバーライドするときにインターフェイスのデフォルト実装を呼び出し、ビジネス ロジックを追加できます。 ######ここまでは順調ですね。ただし、インターフェイスの作成時にデフォルトのメソッドを追加すると、Java コードの互換性がなくなる可能性があります。これは、以下の例から簡単に理解できます。ライブラリが入力としてインターフェイスの 1 つを必要とすると仮定します。
interface SimpleInput { void foo(); void bar(); } abstract class SimpleInputAdapter implements SimpleInput { @Override public void bar() { // some default behavior ... } }
Java 8 より前は、インターフェイスとアダプター クラスを併用する上記の方法と同様、これは Java プログラミングで非常に一般的な設計パターンでした。言語。 。このアダプターは通常、ライブラリーのユーザーが特定の操作を省略できるようにするために、ライブラリー・プロバイダーによって提供されます。ただし、インターフェイスの形式で提供される場合は、多重継承を許可するのと似ています。
さらに、ユーザーが次のアダプターを使用すると仮定します:
class MyInput extends SimpleInputAdapter { @Override public void foo() { // do something ... } @Override public void bar() { super.bar(); // do something additionally ... } }
この実装により、最終的にライブラリと対話できるようになります。 bar メソッドをオーバーライドし、デフォルトの実装に機能を追加する方法に注目してください。
このライブラリを Java 8 に移植するとどうなりますか?まず、ライブラリはアダプター クラスを非推奨にし、デフォルトのメソッドを使用してこの機能を提供する可能性が高くなります。最終的に、インターフェイスは次のようになります。
interface SimpleInput { void foo(); default void bar() { // some default behavior } }
この新しいインターフェイスを使用すると、ユーザーは元のアダプター クラスの代わりにデフォルトのメソッドを使用するようにコードを更新できます。アダプター クラスの代わりにインターフェイスを使用すると、最終的には、そのクラスが特定のアダプターではなく他のクラスを拡張できるようになります。次に、デフォルトのメソッドを使用するために MyInput クラスを練習して移植しましょう。他のクラスを拡張できるようになったので、サードパーティの基本クラスを拡張します。ここではこの基本クラスの役割を気にする必要はありません。これが私たちの関数にとって意味があると想定できます。
class MyInput extends ThirdPartyBaseClass implements SimpleInput { @Override public void foo() { // do something ... } @Override public void bar() { SimpleInput.super.bar(); // do something additionally ... } }
元のクラスと同様の機能を実現するために、Java 8 の新しい構文を使用して、指定されたインターフェイスのデフォルト メソッドを呼び出します。同時に、メソッド内のロジックの一部を基本クラスに移動します。この時点で、あなたは私の肩をたたいて、これは非常に優れたリファクタリングだと言うかもしれません。
私たちはこのライブラリを非常にうまく使用してきました。ただし、メンテナは、より多くの機能を提供するには、別のインターフェイスを追加する必要があります。このインターフェイスは、SimpleInput インターフェイスを継承し、新しいメソッドを追加する ComplexInput インターフェイスに置き換えられます。デフォルト メソッドは一般に安全に追加できるため、メンテナは SimpleInput のデフォルト メソッドをオーバーライドして、より優れたデフォルト メソッドを提供しました。結局のところ、これはアダプター クラスでよく行われることです。
interface ComplexInput extends SimpleInput { void qux(); @Override default void bar() { SimpleInput.super.bar(); // so complex, we need to do more ... } }
新機能は非常に良い結果をもたらしたので、ThirdPartyBaseClass を保守している人々もこのライブラリに依存することにしました。このジョブを実行するために、ThirdPartyLibrary に ComplexInput インターフェイスを実装します。
しかし、これは MyInput クラスにとって何を意味するのでしょうか? ComplexInput インターフェイスを暗黙的に実装するには、ThirdPartyBaseClass クラスを継承できますが、SimpleInput のデフォルト メソッドの呼び出しは突然不正になります。その結果、ユーザーのコードはコンパイルに失敗します。 Java では、直接ではないサブクラスから親クラスのメソッドを呼び出すことは違法であるとみなされるため、この種の呼び出しは現在禁止されています。このデフォルト メソッドは ComplexInput でのみ呼び出すことができますが、そのためにはこのインターフェイスを MyInput で明示的に実装する必要があります。ライブラリのユーザーにとって、この変更は予期されていません。
さらに奇妙なのは、Java ランタイムにはそのような制限が課されていないことです。 JVM のバリデーターを使用すると、クラスが更新された ThirdPartyBaseClass を継承している場合でも、コンパイルされたクラスが SimpleInput::foo メソッドを呼び出すことができるため、暗黙的に ComplexClass が実装されます。この制限はコンパイラにのみ存在します。
以上がJava 8 のデフォルト メソッドはユーザー コードを破壊しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。