web-dev-qa-db-ja.com

デフォルトのメソッドとのインタフェース対Java 8の抽象クラス

Java 8では、 デフォルトメソッド と呼ばれるインタフェースでのメソッドのデフォルト実装が許可されているので、いつabstract classを使用するのかについて混乱があるようです。

それでは、いつデフォルトのメソッドとのインタフェースを使用すべきで、いつ抽象クラスを使用すべきなのでしょうか。そのシナリオでは抽象クラスはまだ有用ですか?

446
Narendra Pathai

抽象メソッドには、デフォルトのメソッド実装(プライベート状態など)よりもはるかに多くのものがありますが、Java 8以降、どちらかを選択するときはいつでも、インターフェースではdefender(別名default)メソッドを使用する必要があります。

デフォルトメソッドの制約は、特定の実装の状態を参照せずに、他のインタフェースメソッドへの呼び出しに関してのみ実装できることです。そのため、主なユースケースは高レベルで便利な方法です。

この新機能の良いところは、以前は便利なメソッドに抽象クラスを使わざるを得なかったため、実装者を単一継承に制限していたのですが、今ではインターフェースと最小限の実装で本当にきれいな設計ができます。努力はプログラマーに強いられました。

defaultメソッドをJava 8に導入しようとする当初の動機は、既存の実装を壊すことなく、Collections Frameworkインターフェースをラムダ指向のメソッドで拡張したいという望みでした。これは公共図書館の作者により関連していますが、あなたのプロジェクトでも同じ機能が役に立つかもしれません。新しい利便性を追加するための場所が1か所に集中しているので、残りの型階層の外観に頼る必要はありません。

263
Marko Topolnik

技術的な違いがいくつかあります。抽象クラスは、Java 8インターフェースと比較してまだもっと多くのことができます。

  1. 抽象クラスはコンストラクタを持つことができます。
  2. 抽象クラスはより構造化されており、状態を保持できます。

概念的には、防御側メソッドの主な目的は、Java 8での(ラムダ関数としての)新しい機能の導入後の下位互換性です。

101
Vadym Vasyliev

これはこの 記事 で説明されています。コレクションのforEachについて考えてください。

List<?> list = …
list.forEach(…);

ForEachはJava.util.ListJava.util.Collectionインターフェースによってまだ宣言されていません。明らかな解決策の1つは、既存のインタフェースに新しいメソッドを追加して、JDKの必要に応じて実装を提供することです。しかし、一度公開されると、既存の実装を壊すことなくインターフェースにメソッドを追加することは不可能です。

デフォルトのメソッドがもたらす利点は、新しいデフォルトのメソッドをインタフェースに追加できるようになり、実装が中断されることがなくなることです。

58
Masudul

この の記事で説明されているように、

Java 8における抽象クラスとインタフェース

Default Methodを導入した後は、インターフェイスと抽象クラスは同じように見えます。しかし、それらはJava 8ではまだ異なる概念です。

抽象クラスはコンストラクタを定義できます。それらはより構造化されており、それらに関連付けられた状態を持つことができます。対照的に、デフォルトメソッドは他のインタフェースメソッドを呼び出すという点でのみ実装でき、特定の実装の状態を参照することはできません。したがって、どちらも異なる目的で使用し、2つを選択することはシナリオのコンテキストによって異なります。

17
Sufiyan Ghori

これら2つはまったく異なります。

デフォルトの方法は、既存のクラスにその状態を変更せずに外部機能を追加することです。

そして抽象クラスは通常の継承型で、拡張されることを意図した通常のクラスです。

14
Andrey Chaschev

のご質問について

それでは、いつデフォルトのメソッドとのインタフェースを使用すべきで、いつ抽象クラスを使用すべきなのでしょうか。そのシナリオでは抽象クラスはまだ有用ですか?

Java のドキュメント が完璧な答えを提供します。

インタフェースと比較した抽象クラス:

抽象クラスはインタフェースに似ています。それらをインスタンス化することはできません、そしてそれらは実装の有無にかかわらず宣言されたメソッドの混合を含むかもしれません。

ただし、抽象クラスでは、静的で最終的ではないフィールドを宣言し、パブリック、保護、およびプライベートの具象メソッドを定義できます。

インターフェースでは、すべてのフィールドは自動的にpublic、static、およびfinalになり、宣言または定義した(デフォルトのメソッドとして)すべてのメソッドはpublicになります。さらに、抽象クラスであるかどうかにかかわらず、1つのクラスのみを拡張できます。一方、インターフェイスはいくつでも実装できます。

それぞれのユースケースは下記のSEポストで説明されています。

インタフェースと抽象クラスの違いは何ですか?

そのシナリオでは抽象クラスはまだ有用ですか?

はい。それらはまだ便利です。それらは非静的、非最終メソッドを含むことができると属性(publicに加えてprotected、private)であり、Java-8インターフェースでさえ不可能です。

12
Ravindra babu

抽象クラスとインタフェースの間で選択があるときはいつでも、いつも(ほとんど)デフォルト(defenderまたは仮想拡張としても知られている)メソッドを好むべきです。

  1. デフォルトメソッドは、古典的なインタフェースパターンと、そのインタフェースのほとんどまたはすべてのメソッドを実装するコンパニオンクラスに終止符を打ちました。例はCollection and AbstractCollectionです。これで、デフォルトの機能を提供するために、インターフェイス自体にメソッドを実装する必要があります。インタフェースを実装するクラスは、メソッドをオーバーライドするか、デフォルトの実装を継承するかを選択できます。
  2. デフォルトメソッドのもう一つの重要な使い方はinterface evolutionです。次のようなクラスBallがあるとします。

    public class Ball implements Collection { ... }

Java 8では、新しい機能が追加されました。インターフェースに追加されたstreamメソッドを使ってストリームを取得することができます。 streamがデフォルトのメソッドではない場合、Collectionインターフェースのすべての実装は、この新しいメソッドを実装していないため、壊れているはずです。デフォルト以外のメソッドをインタフェースに追加することはsource-compatibleではありません。

しかし、クラスを再コンパイルせずに、このクラスBallを含む古いjarファイルを使用するとします。このメソッドがなくてもクラスは正常にロードされ、インスタンスを作成でき、すべて正常に機能しているようです。 BUTプログラムがstreamのインスタンスでBallメソッドを呼び出すと、AbstractMethodErrorが得られます。そのため、メソッドをデフォルトにすることで両方の問題が解決されました。

11
i_am_zero

Javaインタフェースのデフォルトメソッドはインタフェース展開を有効にします。

既存のインターフェースを考えて、古いバージョンのインターフェースとのバイナリ互換性を損なわずにそれにメソッドを追加したい場合は、2つの選択肢があります。実際、インタフェースに追加された抽象メソッドは、このインタフェースを実装するクラスまたはインタフェースによって実装される必要があります。

静的メソッドはクラス固有のものです。デフォルトのメソッドはクラスのインスタンスに固有です。

既存のインタフェースにデフォルトのメソッドを追加した場合、このインタフェースを実装するクラスおよびインタフェースはそれを実装する必要はありません。彼らはすることができます

  • デフォルトのメソッドを実装すると、実装されているインタフェースの実装がオーバーライドされます。
  • 抽象化するメソッドを(実装なしで)再宣言します。
  • 何もしません(その場合、実装されたインターフェースのデフォルトのメソッドは単純に継承されます)。

トピック の詳細はこちら

6
kiriloff

Remi Forax rule isあなたは抽象クラスでデザインしていません。あなたはあなたのアプリをインターフェースでデザインします。 Wateverは、言語が何であれ、Javaのバージョンです。 I面分離原則に支えられていますSOLID 原則。

あとで抽象クラスを使用してコードを因数分解することができます。これで、Java 8では、インターフェースで直接それを行うことができます。これは施設であり、それ以上ではありません。

4
Nicolas Zozol

デフォルトのメソッドとのインタフェースはいつ使用すべきですか、そして抽象クラスはいつ使用すべきですか?

下位互換性:あなたのインターフェースが何百ものクラスで実装されていると想像してください。そのインターフェースを変更すると、たとえそれが変更されてもあなたのインターフェースを実装する他の多くのクラスにとって必須ではないかもしれません、さらにそれはあなたのインターフェースが機能的インターフェースになることを可能にします

事実と制限:

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

2 - 身体を提供する必要があります

3 - インタフェースで使用される他の通常のメソッドのように抽象的であるとは想定されていません。

2
Ahmad Sanie

Java 8では、インタフェースは抽象クラスのように見えますが、次のような違いがあるかもしれません。

1)抽象クラスはクラスであるので、それらはJavaにおけるインタフェースの他の制限に制限されない。抽象クラスは状態を持つことができますが、Javaのインタフェースで状態を持つことはできません。

2)デフォルトメソッドと抽象クラスとのインタフェースのもう1つの意味上の違いは、抽象クラスの内側にコンストラクタを定義できることですが、インタフェースの内側にコンストラクタを定義することはできません。 Java

1
Manish Sahni

他の回答で述べたように、コレクションフレームワークでの下位互換性を提供するために、インターフェイスに実装を追加する機能が追加されました。後方互換性を提供することが、インターフェースに実装を追加する唯一の正当な理由である可能性があると主張します。

それ以外の場合、インターフェイスに実装を追加する場合、インターフェイスが最初に追加された理由の基本法則を破っています。 Javaは、C++とは異なり、単一の継承言語です。多重継承の場合。インターフェイスには、多重継承に伴う問題を引き起こすことなく、多重継承をサポートする言語に付属するタイピングの利点があります。

より具体的には、Javaは実装の単一継承のみを許可しますが、インターフェースの多重継承は許可します。たとえば、次は有効なJavaコードです。

class MyObject extends String implements Runnable, Comparable { ... }

MyObjectは1つの実装のみを継承しますが、3つのコントラクトを継承します。

実装の多重継承には厄介な問題のホストが伴うため、Javaは実装の多重継承を渡しました。これはこの回答の範囲外です。実装の多重継承の問題なしに、コントラクト(別名インターフェース)の多重継承を可能にするインターフェースが追加されました。

私の主張を裏付けるために、本The Java Programming Language、4th editionのKen ArnoldとJames Goslingからの引用を以下に示します。

単一継承は、いくつかの有用で正しい設計を排除します。多重継承の問題は、実装の多重継承から発生しますが、多くの場合、複数の抽象コントラクトとおそらく1つの具体的な実装を継承するために多重継承が使用されます。実装を継承せずに抽象コントラクトを継承する手段を提供すると、複数の実装の継承の問題なしに、複数の継承のタイピングの利点が得られます。抽象コントラクトの継承はinterface inheritanceと呼ばれます。 Javaプログラミング言語は、interface型を宣言できるようにすることで、インターフェイスの継承をサポートします

0
johnnyodonnell

まず開閉原理を考えてください。インタフェースのデフォルトメソッドはそれを無効にします。これはJavaの悪い機能です。それは悪いデザイン、悪いアーキテクチャ、低いソフトウェア品質を助長します。デフォルトのメソッドを完全に使わないようにすることをお勧めします。

いくつか質問をしてみてください。なぜあなたは自分のメソッドを抽象クラスに入れられないのですか?それでは、複数の抽象クラスが必要ですか。それからあなたのクラスが何に責任があるかについて考えてください。あなたがあなたが単一のクラスに置くつもりであるすべてのメソッドが本当に同じ目的を果たすと確信していますか?あなたはいくつかの目的を区別し、それからそれぞれの目的のためにそれ自身のクラスのためにいくつかのクラスにあなたのクラスを分割するでしょう。

0
mentallurg

Javaインタフェースのデフォルトメソッドは、関数のダミー実装を提供するためにもっと使用されるため、たとえそれらが1つだけを処理したいとしても、すべての抽象メソッドを宣言するという苦労からそのインタフェースの実装クラスを節約します。したがって、インタフェースのデフォルトメソッドは、アダプタクラスの概念に代わるものです。

ただし抽象クラスのメソッドは、すべての子クラスが共通の機能をオーバーライドするために必要な場合にのみオーバーライドする必要がある意味のある実装を提供することになっています。

0
shubh