使用するタイミング:Java 8+インターフェースのデフォルトメソッドと抽象メソッド


540

Java 8では、Default Methodsと呼ばれるインターフェースでメソッドのデフォルト実装が可能です。

私は(with )interface default methodではなく、そのようなをいつ使用するのか混乱しています。abstract classabstract method(s)

では、デフォルトのメソッドとのインターフェースをいつ使用し、抽象クラス(抽象メソッドを含む)をいつ使用する必要があるのでしょうか。抽象クラスはそのシナリオでもまだ役に立ちますか?


38
抽象クラスではできますが、インターフェイスにフィールドやプライベートメソッドなどを含めることはできませんか?
sp00m

2
以前このトピックについて疑問に思っていましたが、今ははっきりしています。@ Narendra Pathaiに感謝します。これらの両方が私の疑問だったので、同じトピックについてあなたから尋ねられた別のスレッドのリンクを追加したいと思います。stackoverflow.com/questions/19998309/...
アッシュートッシュランジャン

この記事に関する素晴らしい投稿は、ここにあります:blog.codefx.org/java/everything-about-default-methods
Fabri Pautasso 2016年

基本クラスに状態がある場合でも、基本クラスをインターフェースとしてコーディングできる場合があります。インターフェースが状態のセッターとゲッターを定義し、具象クラスがそれらを実装してフィールドを定義する必要があるだけです。これに対する1つの制限は、抽象クラスでは、Beanプロパティをプライベートまたは保護することができることです。インターフェイスにはパブリックメソッドのみがあります。したがって、抽象基本クラスを使用する1つの理由は、クラスにプライベートまたは保護する必要があるプロパティがある場合です。
DaBlick 2018

@DaBlick HashMapを介してインターフェイスの状態の問題を解決できませんでした。例:int a、b、String cを保持するクラスFooが必要な場合。そして、それらに状態を持たせたい場合は、HashMap </ * Fooオブジェクトの名前* /文字列、/ *フィールドのマップ* / Hashmap </ *名前固有のフィールド* /文字列、/ *フィールド値* /オブジェクト>>マップを作成します。理論上のクラスFooを「インスタンス化」する場合、map.put(nameOfFoo、fields)を実行するメソッドinstantiate(String nameOfFoo)があり、fieldsはHashMap <String、Object> fields.put( "a"、new int( "5")); fields.put( "b"、new int( "6")); fields.put( "c"、 "blah"));
ジョージザビエル

回答:


307

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

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

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

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


34
この理由により、次に追加するのはデフォルトのメソッド宣言です。私はまだこれについては不明ですが、この機能は私にとってハッキングのようであり、誤用のために誰にでも公開されています。
パンサー2015

3
私が見ることができるJava 8時代の抽象クラスの唯一の使用法は、非最終フィールドを定義することです。インターフェースでは、フィールドはデフォルトでfinalであるため、一度割り当てられると変更できません。
Anuroop 2017

7
@Anuroopデフォルトだけでなく、それが唯一のオプションです。インターフェイスはインスタンスの状態を宣言できません。そのため、抽象クラスが存在し続けます。
Marko Topolnik 2017

2
@PhilipRego抽象メソッドは実装がないため、何も呼び出しません。クラスに実装されたメソッドは、クラスの状態(インスタンス変数)にアクセスできます。インターフェイスはそれらを宣言できないため、デフォルトのメソッドはそれらにアクセスできません。状態にアクセスする実装されたメソッドを提供するクラスに依存する必要があります。
Marko Topolnik、2018

2
マルコ・トポルニク、あなたの答えはまだ終わりです。しかし、私はあなたの答えの更新をお勧めしたいと思います。デフォルトのメソッドの優れた点は、インターフェースが新しいデフォルトのメソッドを追加しても、そのインターフェースの以前の実装が壊れないことです。これは、Java 8より前の
バージョンで

125

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

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

概念的には、ディフェンダーメソッドの主な目的は、Java 8に新しい機能(ラムダ関数として)が導入された後の下位互換性です。


20
この答えは、実際に正しく、特に理にかなっている「概念的には、ディフェンダーの方法の主な目的は、下位互換性のある」
しようと

1
@UnKnownこのページはより多くの洞察を提供します:docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
bernie

@UnKnown、基本的には、インターフェイスにメソッドを追加し、そのインターフェイスを実装するクラスがその機能を自動的に取得できるようにします。
LegendLength 2017

3
ポイント番号についてのより微妙なポイント。上記の「状態を保持できるのはこれ」です。抽象クラスは、後で変更できる状態を保持できます。インターフェースは状態を保持することもできますが、インスタンスの作成後に状態が割り当てられると、状態を変更することはできません。
Anuroop 2017

3
@Anuroop public static finalインターフェースのフィールドを「状態」とは記述しません。このstatic部分は、特定のインスタンスにまったく関連していないことを意味します。それらはクラスのインスタンス化に割り当てられますがインスタンスの作成後とは異なります
geronimo 2017

65

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

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

forEachはまだインターフェイスでjava.util.Listも 宣言されてjava.util.Collectionいません。明らかな解決策の1つは、既存のインターフェースに新しいメソッドを追加し、JDKで必要な場所に実装を提供することです。ただし、いったん公開されると、既存の実装を壊さずにメソッドをインターフェースに追加することは不可能です。

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


1
「既存の実装を壊さずにインターフェースにメソッドを追加することは不可能です」-それはそうですか?
Andrey Chaschev 2013年

26
@AndreyChaschev新しいメソッドをインターフェースに追加する場合、すべての実装者はその新しいメソッドを実装する必要があります。したがって、既存の実装を壊します。
Marko Topolnik 2013年

4
@MarkoTopolnikありがとう、それを逃した。ただ言及するだけで、これを部分的に回避する方法があります-このメソッドをデフォルトの抽象実装で提示することによって。この例では、これはをAbstractList::forEachスローしUnsupportedOperationExceptionます。
Andrey Chaschev 2013年

3
@AndreyChaschevはい、それは古い方法でした(khm ... は現在の方法です :)提供された抽象実装からの単一継承に実装者を制限するという欠点があります。
Marko Topolnik 2013年

事前にすべての実装にそのメソッドが含まれている場合でも、問題は発生しません。可能性は低いですが可能です。
ジョージザビエル

19

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

デフォルトのメソッドでは、状態を変更せずに既存のクラスに外部機能追加します。

また、抽象クラスは通常のタイプの継承であり、拡張を目的とした通常のクラスです。


18

この記事で説明したように、

Java 8の抽象クラスとインターフェース

デフォルトメソッドを導入した後、インターフェースと抽象クラスは同じように見えます。ただし、Java 8ではまだ異なる概念です。

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


抽象クラスには、インターフェイスとは異なり定義できるコンストラクタがあると思います。Java 8では、これにより両方とも互いに異なります。
Hemanth Peela

1
インスタンス化できない抽象クラスにコンストラクタがあるのはなぜですか?
ジョージザビエル

抽象クラスのコンストラクターを呼び出す子クラスからsuper()を呼び出すことができます。これは、抽象クラスの状態に影響を与えます。
Sujay Mohan

14

あなたのクエリについて

では、デフォルトのメソッドとのインターフェースをいつ使用し、いつ抽象クラスを使用する必要があるのでしょうか。抽象クラスはそのシナリオでもまだ役に立ちますか?

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

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

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

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

インターフェースを使用すると、すべてのフィールドは自動的にpublic、static、finalになり、宣言または定義した(デフォルトのメソッドとして)すべてのメソッドはpublicになります。また、抽象クラスであるかどうかに関係なく、拡張できるクラスは1つだけですが、任意の数のインターフェイスを実装できます。

それぞれの使用例は、以下のSEポストで説明されています。

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

抽象クラスはそのシナリオでもまだ役に立ちますか?

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


インターフェースにもプライベートメソッドがありますhowtodoinjava.com/java9/java9-p​​rivate-interface-methods
valijon

13

抽象クラスとインターフェースのどちらかを選択する場合は常に、デフォルトの(ディフェンダーまたは仮想拡張とも呼ばれる)メソッドを常に(ほぼ)優先する必要があります。

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

    public class Ball implements Collection { ... }

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

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


9

Javaインターフェースのデフォルトのメソッドは、インターフェースの進化を可能にします

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

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

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

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

トピックの詳細はこちら


7

それは古い質問ですが、私の意見も聞かせてください。

  1. 抽象クラス:抽象クラス内では、子クラスに必要なインスタンス変数を宣言できます

    インターフェース:インターフェース内では、すべての変数は常に静的であり、最後にインスタンス変数を宣言することはできません

  2. 抽象クラス:抽象クラスはオブジェクトの状態について話すことができます

    インターフェース:インターフェースはオブジェクトの状態について話すことはできません

  3. 抽象クラス:抽象クラス内でコンストラクタを宣言できます

    インターフェース:インターフェースの内部で
    コンストラクターの目的はインスタンス変数を初期化することなので、コンストラクターを宣言できませんインターフェイスにインスタンス変数を含めることができない場合、そこでコンストラクタが必要になるのはなぜですか

  4. 抽象クラス:抽象クラス内では、インスタンスと静的ブロックを宣言できます

    インターフェース:インターフェースにインスタンスブロックと静的ブロックを含めることはできません。

  5. 抽象クラス:抽象クラスはラムダ式を参照できません

    インターフェース:単一の抽象メソッドを持つインターフェースはラムダ式を参照できます

  6. 抽象クラス抽象クラス内で、OBJECT CLASSメソッドをオーバーライドできます

    インターフェース:インターフェース内のOBJECT CLASSメソッドをオーバーライドすることはできません。

私は次のことに注意して終わります:

インターフェースのデフォルトのメソッドの概念/静的メソッドの概念は、実装クラスを保存するためだけのものであり、意味のある有用な実装を提供するためのものではありません。デフォルトのメソッド/静的メソッドは、ダミーの実装の一種であり、「使用したい場合、または実装クラスでそれらをオーバーライドできる場合(デフォルトのメソッドの場合)」、したがって、インターフェースの新しいメソッドがいつでも実装クラスに新しいメソッドを実装する必要がなくなります。追加されます。したがって、インターフェースを抽象クラスと同じにすることはできません。


5

Remi Foraxのルールは、抽象クラスで設計しないことです。アプリはインターフェースを使用して設計します。Wateverは、言語が何であれ、Javaのバージョンです。これは、SOL I D原則のインターフェース分離原則に裏付けられてます

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


2

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

下位互換性: インターフェースが数百のクラスによって実装されていると想像してください。そのインターフェースを変更すると、すべてのユーザーが新しく追加されたメソッドを実装するように強制されます。されるように機能インタフェース

事実と制限:

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

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

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


1

Java 8では、インターフェイスは抽象クラスのように見えますが、次のような違いがある場合があります。

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

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


#2に同意しますが、#1については、インターフェースを実装するだけで実装クラスを介して状態を保持できないのですか?
ジョージザビエル

0

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

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


0

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

それ以外の場合、インターフェースに実装を追加すると、インターフェースが最初に追加された理由に関する基本法則に違反することになります。Javaは、複数の継承を可能にするC ++とは異なり、単一の継承言語です。インターフェイスは、多重継承に伴う問題を引き起こさずに、多重継承をサポートする言語に付属する型付けの利点を提供します。

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

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

MyObject 継承は1つだけですが、3つのコントラクトを継承します。

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

私の主張を裏付けるために、ここに本「Javaプログラミング言語、第4版」からのケン・アーノルドとジェームズ・ゴスリングの引用があります:

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


-1

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.