Javaにおける抽象クラスとインタフェースの違い
抽象クラスとインターフェースの違いは、人気のある面接の質問の一つです。抽象クラスとインターフェースは、Javaプログラミング言語の中核的な要素です。インターフェースを選ぶか、抽象クラスを選ぶかは、アーキテクトが行うデザインの決定です。私の過去の記事では、Javaのインターフェースと抽象クラスについてできる限りの詳細を提供してきました。この記事では、抽象クラスとインターフェースの違い、そしていつインターフェースを使うべきか、逆に抽象クラスを使うべきかについて学びます。
抽象クラスとインターフェースの違いは何ですか?
-
- 抽象キーワードは抽象クラスを作成するために使用され、メソッドとも使用できますが、インターフェースキーワードはインターフェースを作成するために使用され、メソッドとは使用できません。
-
- サブクラスは、抽象クラスを拡張するためにextendsキーワードを使用し、サブクラスが抽象クラスでない限り、抽象クラスで宣言されたすべてのメソッドの実装を提供する必要があります。一方、サブクラスはインターフェースを実装するためにimplementsキーワードを使用し、インターフェースで宣言されたすべてのメソッドの実装を提供する必要があります。
-
- 抽象クラスは実装を持つメソッドを持つことができますが、インターフェースは絶対的な抽象化を提供し、メソッドの実装を持つことはできません。なお、Java 8以降では、メソッドの実装を含むデフォルトおよび静的メソッドをインターフェースに作成することができます。
-
- 抽象クラスはコンストラクタを持つことができますが、インターフェースはコンストラクタを持つことはできません。
-
- 抽象クラスは通常のJavaクラスのすべての機能を持っていますが、インスタンス化することはできません。クラスを抽象化するためにabstractキーワードを使用できますが、インターフェースは完全に異なるタイプであり、public static finalの定数とメソッド宣言のみを持つことができます。
-
- 抽象クラスのメソッドはpublic、private、protected、staticなどのアクセス修飾子を持つことができますが、インターフェースのメソッドは暗黙的にpublicおよびabstractであり、他のアクセス修飾子を使用することはできません。
-
- サブクラスは1つの抽象クラスしか拡張できませんが、複数のインターフェースを実装することができます。
-
- 抽象クラスは他のクラスを拡張し、インターフェースを実装することができますが、インターフェースは他のインターフェースのみを拡張することができます。
-
- 抽象クラスにmain()メソッドがある場合は実行できますが、インターフェースは実行できません。なぜなら、インターフェースはmainメソッドの実装を持つことができないからです。
- インターフェースはサブクラスのための契約を定義するために使用されますが、抽象クラスも契約を定義しますが、サブクラスが使用する他のメソッドの実装を提供することができます。
インターフェースと抽象クラスの違いについては以上です。次に、いつインターフェースを使い、いつ抽象クラスを使うべきか、その逆の場合について説明します。
インターフェースまたは抽象クラス
インターフェースと抽象クラスのどちらをサブクラスに対する契約の提供に選ぶかは、デザインの決定であり、多くの要素に依存します。インターフェースが最適な選択肢となる場合と、抽象クラスを利用できる場合について見てみましょう。
-
- Javaは複数のクラスレベルの継承をサポートしていませんので、各クラスは1つのスーパークラスのみを拡張することができます。しかし、クラスは複数のインターフェースを実装することができます。そのため、ほとんどの場合、インターフェースはクラスの階層と契約の基礎として良い選択肢です。また、インターフェースを使用したコーディングは、Javaでのコーディングのベストプラクティスの一つです。
契約に多くのメソッドがある場合、抽象クラスはより便利です。なぜなら、すべてのサブクラスに共通のメソッドに対してデフォルトの実装を提供することができるからです。また、サブクラスが特定のメソッドを実装する必要がない場合、実装を省略することができますが、インターフェースの場合、サブクラスはすべてのメソッドの実装を提供しなければなりません。
基本的な契約が変更される可能性がある場合、インターフェースは問題を引き起こす可能性があります。なぜなら、実装クラス全体を変更せずにインターフェースに追加メソッドを宣言することはできないからです。抽象クラスではデフォルトの実装を提供し、新しいメソッドを実際に使用する実装クラスのみを変更することができます。
抽象クラスとインターフェースの両方を使用する
システムの設計において、インターフェースと抽象クラスを組み合わせることが最良のアプローチです。たとえば、JDKのjava.util.Listは多くのメソッドを含むインターフェースであり、そのためにjava.util.AbstractListという抽象クラスが存在し、Listインターフェースのすべてのメソッドに対する枠組みの実装を提供しています。これにより、任意のサブクラスはこのクラスを拡張し、必要なメソッドのみを実装することができます。常に基本となるインターフェースから開始し、すべてのサブクラスが実装すべきメソッドを定義し、一部のメソッドを特定のサブクラスのみが実装する必要がある場合には、基本インターフェースを拡張し、それらのメソッドを持つ新しいインターフェースを作成するべきです。サブクラスは要件に応じて基本インターフェースまたは子インターフェースのどちらを実装するかを選択することができます。メソッドの数が多くなってきた場合には、インターフェースと抽象クラスの両方を提供し、サブクラスに柔軟性を与えるために、子インターフェースを実装する骨格の抽象クラスを作成することも悪くありません。
Java 8 インターフェースの変更
Java 8以降、インターフェース内でメソッドの実装をすることができます。インターフェース内ではデフォルトメソッドと静的メソッドを作成し、それらに実装を提供することができます。これにより、抽象クラスとインターフェースの差を埋め、新しいメソッドのデフォルト実装を提供することで、インターフェースをさらに拡張することが可能となりました。詳細については、Java 8のインターフェースのデフォルト静的メソッドをご覧ください。