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


87

私は質問をされました、私は私の答えをここでレビューしてもらいたいと思いました。

Q:インターフェースを実装するよりも、抽象クラスを拡張する方が適切なシナリオはどれですか?

A:テンプレートメソッドデザインパターンを使用している場合。

私は正しいですか?

質問を明確に述べることができなかった場合は申し訳ありません。
抽象クラスとインターフェイスの基本的な違いを知っています。

1)特定の操作(メソッドの実装)ではすべてのサブクラスに同じ機能を実装し、他のいくつかの操作(メソッドシグネチャのみ)では異なる機能を実装する必要がある場合は、抽象クラスを使用します

2)インターフェースの実装に準拠できるように、署名を同じ(および実装が異なる)にする必要がある場合は、インターフェースを使用します

3)最大1つの抽象クラスを拡張できますが、複数のインターフェースを実装できます

質問を繰り返します:上記のシナリオ以外に、特に抽象クラスを使用する必要があるシナリオはありますか(テンプレートメソッドのデザインパターンは概念的にこれのみに基づいていることがわかります)?

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

これら2つのどちらを選択するかは、実際には何をしたいかによって異なりますが、幸いなことに、ErichGammaは私たちを少し助けてくれます。

常にトレードオフがありますが、インターフェイスは基本クラスに関して自由を与え、抽象クラスは後で新しいメソッドを追加する自由を与えます。–エーリヒ・ガンマ

コード内の他の多くのものを変更せずにインターフェイスに移動して変更することはできないため、これを回避する唯一の方法は、まったく新しいインターフェイスを作成することです。これは必ずしも良いことではない場合があります。

Abstract classes主に密接に関連するオブジェクトに使用する必要があります。Interfaces無関係なクラスに共通の機能を提供するのに優れています。




これは重複していません。OPは、インターフェイスを実装するのではなく、抽象クラスをいつ拡張するかを知りたいと考えています。彼は、抽象クラスまたはインターフェースをいつ作成するかを知りたくありません。彼の抽象クラスとインターフェースはすでに書かれています。Hdは、拡張するか実装するかを知りたがっています。
Shiplu Mokaddim 2012

1
@ shiplu.mokadd.imそれは違いのない区別です。抽象クラスを拡張せずに使用することはできません。ここでのあなたのつまらないことは完全に無意味に思えます。
ローン侯爵2012

回答:


86

いつインターフェースを使用するか

インターフェイスを使用すると、誰かが最初からインターフェイスを実装したり、元の目的または主な目的がインターフェイスとはまったく異なる他のコードでインターフェイスを実装したりできます。彼らにとって、あなたのインターフェースは偶発的なものであり、あなたのパッケージを使用できるようにするために彼らのコードに追加しなければならないものです。欠点は、インターフェイスのすべてのメソッドをパブリックにする必要があることです。すべてを公開したくない場合があります。

抽象クラスを使用する場合

対照的に、抽象クラスはより多くの構造を提供します。通常、いくつかのデフォルトの実装を定義し、完全な実装に役立ついくつかのツールを提供します。キャッチは、それを使用するコードはベースとしてクラスを使用する必要があるということです。あなたのパッケージを使いたい他のプログラマーがすでに独自のクラス階層を独自に開発している場合、それは非常に不便かもしれません。Javaでは、クラスは1つの基本クラスからのみ継承できます。

両方を使用する場合

インターフェイスと抽象クラスの両方の長所を提供できます。実装者は、必要に応じて抽象クラスを無視できます。これを行うことの唯一の欠点は、インターフェイス名を介してメソッドを呼び出すことは、抽象クラス名を介してメソッドを呼び出すよりもわずかに遅いことです。


OPは、インターフェイスを実装するのではなく、抽象クラスを拡張するタイミングを知りたいと思います
Shiplu Mokaddim 2012

@ shiplu.mokadd.im実際、OPは非常に具体的な質問をしましたが、その答えは「はい」または「いいえ」のいずれかです。
ローン侯爵2012

4
あなたが正しい。しかし、SOでは、適切な説明で「はい/いいえ」と答えます。
Shiplu Mokaddim 2012

1
@ shiplu.mokadd.imそれがあなたに彼の質問を誤って述べる許可を与える方法がわかりません。
ローン侯爵2012

この単一の声明に基づいてのみIf we are using template method design pattern私たちは言うことがYESできませんまたはNO
DivineDesert 2012

31

質問を繰り返します:特に抽象クラスを使用する必要がある上記以外のシナリオがあります(テンプレートメソッドのデザインパターンは概念的にこれのみに基づいています)

はい、JAXBを使用している場合。それはインターフェースが好きではありません。抽象クラスを使用するか、ジェネリックでこの制限を回避する必要があります。

個人のブログ投稿から

インターフェース:

  1. クラスは複数のインターフェースを実装できます
  2. インターフェイスはコードをまったく提供できません
  3. インターフェイスは、パブリック静的最終定数のみを定義できます
  4. インターフェイスはインスタンス変数を定義できません
  5. 新しいメソッドを追加すると、クラスの実装に波及効果があります(設計保守)
  6. JAXBはインターフェースを処理できません
  7. インターフェイスは抽象クラスを拡張または実装できません
  8. すべてのインターフェースメソッドはパブリックです

一般に、インターフェースを使用してコントラクトを定義する必要があります(どのように達成するかではなく、何を達成するか)。

抽象クラス:

  1. クラスは最大で1つの抽象クラスを拡張できます
  2. 抽象クラスにはコードを含めることができます
  3. 抽象クラスは静的定数とインスタンス定数の両方を定義できます(最終)
  4. 抽象クラスはインスタンス変数を定義できます
  5. 既存の抽象クラスコードの変更は、クラスの拡張に波及効果をもたらします(実装の保守)
  6. 抽象クラスに新しいメソッドを追加しても、クラスの拡張に波及効果はありません
  7. 抽象クラスはインターフェースを実装できます
  8. 抽象クラスはプライベートメソッドと保護されたメソッドを実装できます

(部分的な)実装には抽象クラスを使用する必要があります。これらは、APIコントラクトの実装方法を制限する手段になる可能性があります。


3
インターフェイス#8のJava 8ではdefaultstaticメソッドも使用できます。
初心者ユーザー

15

インターフェイスは、すべてのクラスが同じ構造であるが、機能がまったく異なるシナリオがある場合に使用されます。

抽象クラスは、すべてのクラスが同じ構造であるが、いくつかは同じでいくつかの異なる機能を持っているというシナリオがある場合に使用されます。

記事を見てください:http//shoaibmk.blogspot.com/2011/09/abstract-class-is-class-which-cannot-be.html


9

ここには素晴らしい答えがたくさんありますが、インターフェイスと抽象クラスの両方を使用するのが最善の方法であることがよくあります。 この不自然な例を考えてみましょう。

あなたは投資銀行のソフトウェア開発者であり、市場に注文を出すシステムを構築する必要があります。あなたのインターフェース、トレーディングシステム何をするかについての最も一般的な考えを捉えています

1) Trading system places orders
2) Trading system receives acknowledgements

インターフェースでキャプチャでき、 ITradeSystem

public interface ITradeSystem{

     public void placeOrder(IOrder order);
     public void ackOrder(IOrder order);

}

これで、セールスデスクや他のビジネスラインで働くエンジニアがシステムとのインターフェースを開始して、既存のアプリに注文機能を追加できるようになりました。そして、あなたはまだ構築を始めていません!これがインターフェースの力です。

それで、あなたは先に進んで、トレーダーのためのシステムを構築します。彼らはあなたのシステムが安い株を見つける機能を持っていると聞いて、それを試してみることに非常に熱心です!この動作をと呼ばれる方法でキャプチャfindGoodDeals()しますが、市場への接続に関係する厄介なものがたくさんあることにも気づきます。たとえば、あなたが開く必要がありSocketChannel

public class StockTradeSystem implements ITradeSystem{    

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

具体的な実装は次のようにこれらの厄介な方法をたくさん持ってしようとしているconnectToMarket()が、findGoodDeals()すべてのトレーダーは、実際に気にしています。

ここで、抽象クラスが活躍します。 上司は、通貨トレーダーもあなたのシステムを使いたいとあなたに知らせます。通貨市場を見ると、配管は株式市場とほぼ同じであることがわかります。実際、connectToMarket()外国為替市場に接続するために逐語的に再利用することができます。ただし、findGoodDeals()通貨の分野ではまったく異なる概念です。したがって、コードベースを海の向こう側の外国為替ウィズキッドに渡す前に、まずabstractクラスにリファクタリングし、findGoodDeals()実装されないままにします

public abstract class ABCTradeSystem implements ITradeSystem{    

    public abstract void findGoodDeals();

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

あなたの株取引システムfindGoodDeals()はあなたがすでに定義したように実装します、

public class StockTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

しかし今では、FXの名手はfindGoodDeals()、通貨の実装を提供するだけでシステムを構築できます。彼女はソケット接続やインターフェースメソッドを再実装する必要はありません!

public class CurrencyTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       ccys = <Genius stuff to find undervalued currencies>
       System.out.println("The best FX spot rates are: " + ccys);
    }

インターフェイスへのプログラミングは強力ですが、同様のアプリケーションは、ほとんど同じ方法でメソッドを再実装することがよくあります。抽象クラスを使用すると、インターフェイスの能力を維持しながら、再実装を回避できます。

注:なぜfindGreatDeals()がインターフェースの一部ではないのか不思議に思うかもしれません。インターフェースは、取引システムの最も一般的なコンポーネントを定義することを忘れないでください。別のエンジニアは、完全に異なる取引システムを開発する場合があります。このシステムでは、良い取引を見つけることを気にしません。このインターフェースは、セールスデスクがシステムにもインターフェースできることを保証するため、「お得な」などのアプリケーションの概念にインターフェースを絡ませないことが望ましいです。


6

抽象クラスとインターフェースのどちらを使用する必要がありますか?

これらのステートメントのいずれかがユースケースに当てはまる場合は、抽象クラスの使用を検討してください。

密接に関連するいくつかのクラス間でコードを共有したいとします。

抽象クラスを拡張するクラスには、多くの一般的なメソッドまたはフィールドがあるか、パブリック以外のアクセス修飾子(protectedやprivateなど)が必要であることが期待されます。

非静的または非最終フィールドを宣言する必要があります。これにより、メソッドが属するオブジェクトの状態にアクセスして変更できるメソッドを定義できます。

これらのステートメントのいずれかがユースケースに当てはまる場合は、インターフェイスの使用を検討してください。

無関係なクラスがインターフェースを実装することを期待します。たとえば、ComparableおよびCloneableインターフェースは、多くの無関係なクラスによって実装されています。

特定のデータ型の動作を指定したいが、その動作を誰が実装するかについては気にしない。

型の多重継承を利用したい。

http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html


4

過去3年間で、Java 8リリースとのインターフェースに新しい機能が追加され、状況は大きく変化しました。

インターフェイスのOracleドキュメントページから:

インターフェイスは、クラスと同様の参照型であり、定数、メソッドシグネチャ、デフォルトメソッド、静的メソッド、およびネストされた型のみを含めることができます。メソッド本体は、デフォルトのメソッドと静的メソッドにのみ存在します。

質問で引用したように、抽象クラスはテンプレートメソッドパターンに最適ですスケルトンを作成する必要があるです。ここではインターフェースを使用できません。

インターフェイスよりも抽象クラスを優先するもう1つの考慮事項:

基本クラスには実装がなく、サブクラスのみが独自の実装を定義する必要があります。サブクラスと状態を共有したいので、インターフェースの代わりに抽象クラスが必要です。

抽象クラスは、関連するクラス間の「is a」関係を確立し、インターフェースは、関連しないクラス間の「hasa」機能を提供します


質問の2番目の部分については、java-8リリースより前のjavaを含むほとんどのプログラミング言語に有効です。

常にトレードオフがありますが、インターフェイスは基本クラスに関して自由を与え、抽象クラスは後で新しいメソッドを追加する自由を与えます。–エーリヒ・ガンマ

コード内の他の多くのものを変更せずにインターフェイスに移動して変更することはできません

上記の2つの考慮事項を考慮して、以前にインターフェースするよりも抽象クラスを好む場合は、デフォルトのメソッドがインターフェースに強力な機能を追加したため、今すぐ再考する必要があります。

デフォルトのメソッドを使用すると、ライブラリのインターフェイスに新しい機能を追加し、それらのインターフェイスの古いバージョン用に記述されたコードとのバイナリ互換性を確保できます。

インターフェイスと抽象クラスの間でそれらの1つを選択するには、Oracleのドキュメントページで次のように引用します。

抽象クラスはインターフェースに似ています。それらをインスタンス化することはできません。また、実装の有無にかかわらず宣言されたメソッドが混在している可能性があります。ただし、抽象クラスを使用すると、静的および最終ではないフィールドを宣言し、パブリック、プロテクト、およびプライベートの具象メソッドを定義できます。

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

詳細については、これらの関連する質問を参照してください。

インターフェイスと抽象クラス(一般的なOO)

InterfaceクラスとAbstractクラスの違いをどのように説明すればよいですか?

要約すると、バランスは現在、インターフェースに向かって傾いています

上記のシナリオ以外に、特に抽象クラスを使用する必要があるシナリオはありますか(テンプレートメソッドのデザインパターンは概念的にこれのみに基づいていることがわかります)?

一部のデザインパターンは、テンプレートメソッドパターンとは別に、(インターフェイスを介して)抽象クラスを使用します。

作成パターン:

Abstract_factory_pattern

構造パターン:

Decorator_pattern

行動パターン:

Mediator_pattern


これ:「抽象クラスは関連するクラス間の「isa」関係を確立し、インターフェースは無関係なクラス間の「hasa」機能を提供します。」
ガブリエル

3

あなたは正しくありません。多くのシナリオがあります。それを単一の8ワードルールに減らすことは不可能です。


1
あなたが漠然としているのでない限り、できる限りインターフェースを使用してください;)
Peter Lawrey 2012

@PeterLawreyええ、循環論法があなたを遅くさせないでください;-)
ローンの侯爵

結局のところ、これは「スタックオーバーフロー」です。;)私のポイントは、より単純なインターフェースを使用できるのであれば、そうすることです。それ以外の場合は、抽象クラスを使用する以外に選択肢はありません。それほど複雑ではないと思います。
Peter Lawrey 2012

私はあなたがより建設的なアイデアを提供できると思います。いくつかの代表的なシナリオについて話すように/
Adams.H 2014

3

最短の答えは、uouが求める機能のいくつかがすでに実装されている場合に抽象クラスを拡張することです。

インターフェイスを実装する場合は、すべてのメソッドを実装する必要があります。ただし、抽象クラスの場合、実装する必要のあるメソッドの数は少なくなる可能性があります。

では、テンプレートのデザインパターン定義された動作がなければなりません。この動作は、抽象的である他の方法に依存します。サブクラスを作成してそれらのメソッドを定義することにより、実際にメインの動作を定義します。インターフェイスは何も定義せず、宣言するだけなので、基本的な動作をインターフェイスに含めることはできません。したがって、テンプレートデザインパターンには常に抽象クラスが付属しています。ビヘイビアーのフローをそのまま維持したい場合は、抽象クラスを拡張する必要がありますが、メインビヘイビアーをオーバーライドしないでください。


Pure Virtual Functionの追加リファレンスでは、抽象クラスとインターフェイスの収束に関する洞察がさらに追加されます。 Pure virtual functions can also be used where the method declarations are being used to define an interface - similar to what the interface keyword in Java explicitly specifies. In such a use, derived classes will supply all implementations. In such a design pattern, the abstract class which serves as an interface will contain only pure virtual functions, but no data members or ordinary methods. パート(1/2)
Abhijeet 2015

パート(2/2)抽象クラスとインターフェースの相違は、上記の最後の行no data members or ordinary methods[抽象クラス]で説明されています。
アビジート2015

3

私の意見では、基本的な違いはそれan interface can't contain non abstract methods while an abstract class canです。したがって、サブクラスが共通の動作を共有する場合、この動作はスーパークラスに実装され、サブクラスに継承されます。

また、「Javaのソフトウェアアーキテクチャ設計ppatterns」の本から次のように引用しました。

「Javaプログラミング言語では、多重継承はサポートされていません。つまり、クラスは1つのクラスからのみ継承できます。したがって、継承は絶対に必要な場合にのみ使用する必要があります。可能な場合は常に、共通の動作を示すメソッドをで宣言する必要があります。さまざまな実装クラスによって実装されるJavaインターフェイスの形式。ただし、インターフェイスにはメソッド実装を提供できないという制限があります。つまり、インターフェイスのすべての実装者は、インターフェイスで宣言されたすべてのメソッドを明示的に実装する必要があります。メソッドは機能の不変部分を表し、すべての実装者クラスでまったく同じ実装を持っています。これにより、コードが冗長になります。次の例は、冗長なメソッドの実装を必要とせずに、このような場合に抽象親クラスパターンを使用する方法を示しています。」


2

抽象クラスは、2つの重要な点でインターフェースとは異なります

  • それらは選択されたメソッドのデフォルトの実装を提供します(それはあなたの答えでカバーされています)
  • 抽象クラスは状態(インスタンス変数)を持つことができます-したがって、これはインターフェースの代わりにそれらを使用したいもう1つの状況です

インターフェイスには変数を含めることができますが、デフォルトでは最終的なものです。
Tomasz Mularczyk 2016年

1

これは良い質問です。これらの2つは似ていませんが、書き直しなど、同じ理由で使用できます。作成するときは、インターフェイスを使用するのが最適です。クラスに関して言えば、デバッグに適しています。


0

Abstract classes should be extended when you want to some common behavior to get extended。抽象スーパークラスは共通の動作を持ち、サブクラスが実装する必要がある抽象メソッド/特定の動作を定義します。

Interfaces allows you to change the implementation anytime allowing the interface to be intact


0

これは私の理解です、これが役立つことを願っています

抽象クラス:

  1. 継承されるメンバー変数を持つことができます(インターフェイスでは実行できません)
  2. コンストラクターを持つことができます(インターフェースは持つことができません)
  3. そのメソッドは任意の可視性を持つことができます(つまり、プライベート、保護など-すべてのインターフェイスメソッドはパブリックです)
  4. メソッドを定義できます(実装されたメソッド)

インターフェイス:

  1. 変数を持つことができますが、それらはすべてパブリック静的最終変数です
    • 静的スコープでは決して変化しない定数値
    • 非静的変数にはインスタンスが必要であり、インターフェイスをインスタンス化することはできません
  2. すべてのメソッドは抽象的です(抽象メソッドにコードはありません)
    • すべてのコードは、特定のインターフェイスを実装するクラスで実際に作成する必要があります

0

抽象化とインターフェースの使用法:

1つは「Is-A-Relationship」を持ち、もう1つは「Has-A-Relationship」を持っています

デフォルトのプロパティは抽象で設定されており、追加のプロパティはインターフェイスを介して表現できます。

例:->人間には、食事、睡眠などのデフォルトのプロパティがいくつかありますが、水泳、遊びなどの他のカリキュラムアクティビティがある場合は、インターフェイスで表現できます。

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