0
2
Javaの抽象クラスとインタフェースの使い分け
抽象クラスとインターフェースの違いについてある程度理解できたのですが、
実際にどういったシチュエーションで用いられるのか、その区別がつきません。
例えば、
抽象クラスは抽象メソッドに加え、具象メソッドを記述でき、
インタフェースにおいても、defaultを用いれば処理を書ける、
のであれば、抽象クラスで出来ることがインタフェースを使っても実現できる、と思うのです。
抽象クラスとインタフェースで、修飾子や多重継承などの違いがあることは理解できたのですが、
具体例が思いつかなくて、現状の理解度に満足していません。
この機能を抽象クラスで実装しよう、インタフェースで実装しようと決めてになるのは、どういったシチュエーションですか?
具体的な例をあげてもらえると嬉しいです。
質問内容をまとめます
抽象クラスとインターフェースの具体的な利用方法は?
以上、よろしくお願い致します。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
回答4件
#1
総合スコア16598
投稿2023/05/31 11:51
編集2023/05/31 11:54抽象クラスで出来ることがインタフェースを使っても実現できる、と思うのです。
だったら使い分けとか考えず、ぜんぶインタフェースにすればいい。
※ 昔のインタフェースはdefaultメソッドが書けなかったから抽象クラスが重宝されたけど。
ただし、インタフェースに定義するメソッドはすべてpublicになっちゃうから、
隠したいメソッドがあるときはアレですが。
#2
総合スコア2123
投稿2023/05/31 15:53
個人的な見解ですが、インターフェースは"定義"のために利用します。たとえば製品情報を検索するサービスがあるとします。このサービスの利用者は製品情報がDBで管理されているのか、CSVファイルで管理されているのか、そういった具体的な実装については気にする必要はありません。利用者の関心はあくまで「製品IDを渡せば製品情報が取得できる」「すべての製品情報を取得できる」といったサービスのふるまい・定義にあります。
java
1public interface ProductSearchService { 2 Product find(String productId); 3 List<Product> findAll(); 4} 5 6// 本番環境ではDBで製品情報を管理する。 7public DBProductService implements ProductService { ... }; 8 9// テスト環境ではCSVで製品情報を管理する。 10public CSVProductService implements ProductService { ... };
一方、抽象クラスは"共通処理"のために利用します。製品情報を検索するクラス(ProductSearchService)とユーザ情報を検索するクラス(UserSearchService)があるとします。このふたつのクラスでは「DBにSELECT文を発行し結果を取得する」という処理が共通しています。この共通処理を抽象クラスに実装し、アクセス修飾子をprotectedとすることで、ProductSearchServiceとUserSearchService以外で利用させないようにします。
java
1public abstract class AbstractSearchService <T> { 2 protected List<T> executeSelectQuery(String query) { ... } 3} 4 5public class UserSearchService extends AbstractSearchService<User> { 6 public List<User> findAll() { /** executeSelectQueryを利用した処理 */ } 7} 8 9public class ProductSearchService extends AbstractSearchService<Product> { 10 public List<Product> findAll() { /** executeSelectQueryを利用した処理 */ } 11}
ただ、現代的なJavaプログラミングにおいては、抽象クラスはほとんど利用されなくなっているというのが個人的な感想です(インターフェースは利用されている)。共通処理のために抽象クラスを利用すると書きましたが、処理の共通化のために継承を利用することは推奨されません。そもそも現代的なオブジェクト指向プログラミングでは継承そのもの利用を避ける傾向にあります。これについてはJavaプログラミングにおいてもっとも有名なベストプラクティス集のひとつ『Effective Java 第3版』において次のような章がありますので、参考にしてみてください。
- 項目18 継承よりもコンポジションを選ぶ
- 項目19 継承のために設計および文書化する、でなければ継承を禁止する
- 項目20 抽象クラスよりもインタフェースを選ぶ
#3
総合スコア1069
投稿2023/06/01 10:01
編集2023/06/01 10:53総合スコア2123
投稿2023/05/31 15:53
個人的な見解ですが、インターフェースは"定義"のために利用します。たとえば製品情報を検索するサービスがあるとします。このサービスの利用者は製品情報がDBで管理されているのか、CSVファイルで管理されているのか、そういった具体的な実装については気にする必要はありません。利用者の関心はあくまで「製品IDを渡せば製品情報が取得できる」「すべての製品情報を取得できる」といったサービスのふるまい・定義にあります。
java
1public interface ProductSearchService { 2 Product find(String productId); 3 List<Product> findAll(); 4} 5 6// 本番環境ではDBで製品情報を管理する。 7public DBProductService implements ProductService { ... }; 8 9// テスト環境ではCSVで製品情報を管理する。 10public CSVProductService implements ProductService { ... };
一方、抽象クラスは"共通処理"のために利用します。製品情報を検索するクラス(ProductSearchService)とユーザ情報を検索するクラス(UserSearchService)があるとします。このふたつのクラスでは「DBにSELECT文を発行し結果を取得する」という処理が共通しています。この共通処理を抽象クラスに実装し、アクセス修飾子をprotectedとすることで、ProductSearchServiceとUserSearchService以外で利用させないようにします。
java
1public abstract class AbstractSearchService <T> { 2 protected List<T> executeSelectQuery(String query) { ... } 3} 4 5public class UserSearchService extends AbstractSearchService<User> { 6 public List<User> findAll() { /** executeSelectQueryを利用した処理 */ } 7} 8 9public class ProductSearchService extends AbstractSearchService<Product> { 10 public List<Product> findAll() { /** executeSelectQueryを利用した処理 */ } 11}
ただ、現代的なJavaプログラミングにおいては、抽象クラスはほとんど利用されなくなっているというのが個人的な感想です(インターフェースは利用されている)。共通処理のために抽象クラスを利用すると書きましたが、処理の共通化のために継承を利用することは推奨されません。そもそも現代的なオブジェクト指向プログラミングでは継承そのもの利用を避ける傾向にあります。これについてはJavaプログラミングにおいてもっとも有名なベストプラクティス集のひとつ『Effective Java 第3版』において次のような章がありますので、参考にしてみてください。
- 項目18 継承よりもコンポジションを選ぶ
- 項目19 継承のために設計および文書化する、でなければ継承を禁止する
- 項目20 抽象クラスよりもインタフェースを選ぶ
Swing
Swingのパッケージにデフォルトの振舞いをもつ抽象クラスがあります。目的はエンドユーザーが自力で複雑な処理を書く負担を軽減するためです。
Template Method パターン
Template Method パターンでは、抽象クラスのfinalなメソッドが呼ばれると、その中で自身の抽象メソッドを呼びます。finalなメソッドは内部の制御の流れを固定します。サブクラスは抽象メソッドをオーバーライドして、ポリモーフィズムを実現します。
しかし関数型インターフェイスが登場してから抽象メソッドを関数オブジェクトで置き換えることが推奨されるようになりました。『Effective Java 第3版』 項目44 標準の関数型インターフェースを使う
抽象メソッドをオーバーライドするより関数オブジェクトをインジェクションするという意味のようです。
#4
総合スコア4818
投稿2023/06/06 06:44
基本的にはインターフェースでこまらないならインターフェースでいいと思います。
使い分けは、インターフェースや抽象クラスそのものよりも・・・
それを利用するメソッド
たとえば、
void someMethod(someInterface a)
なのか
void someMethod(someSubclass a)
なのかな気がします。
このときインターフェースを引数として受け取るのか、抽象クラスのサブクラスで受け取るのかは
拡張性と、担保している機能で変わると考えています。
具体的にいうと・・・・
インターフェースのメソッドはfinalにできないため、特定の実装が求められるときにオーバーライドされると困ってしまうことがあります。
これは委譲されて呼ばれればOKとはいえ、わざわざ不具合を混入させる可能性のある移譲を選択するよりはfinalなメソッドを提供する方がよいでしょう。
同じタグがついた質問を見る
Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。
オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。