導入
こんにちは、もんすんです。
皆さんは、インターフェースと抽象クラスをうまく使い分けできていますか?
Javaでは、インタフェースと抽象クラスの2つの仕組みを使って、複数の型(クラス)の共通の振る舞いを定義することができます。
今回は、これらの違いや使いどころを初学者にもわかりやすく解説していこうと思います。
インタフェースと抽象クラスの基本的な違い
まず、インターフェースと抽象クラスの概要について解説します。
インタフェース
- 実装するクラスに共通する「仕様」を定義します
- クラスは複数のインタフェースを実装できます
- 多重実装が可能と言ったりします
- デフォルトメソッドを使うことで、メソッドの具体的な実装も提供できます(Java8以降)
抽象クラス
- クラスに共通の「基本的な実装」を提供します
- こちらはクラスのため、継承になりますが、継承するクラスが継承できるのは1つのクラスに限定されます
- 多重継承不可と言ったりします
- フィールド(変数)やコンストラクタを定義できます
それぞれが適する場面
インタフェースが適している場面
多重継承が必要な場合
Javaではクラスの多重継承はできませんが、インタフェースなら複数の型を実装できます。例えば、次のように「開発者」と「デザイナー」のような複数の属性を持つクラスを定義できます。
public interface Developer {
void develop();
}
public interface Designer {
void design();
}
public interface DevDesigner extends Developer, Designer {
void create();
}
既存のクラスを拡張せずに新しい振る舞いを追加する場合
既存のクラスにインタフェースを実装させるのは簡単です。
たった以下の2つのステップのみで済みます。
- 必要なメソッドの追加
- implementsキーワードの追加
これだけで新しい仕様を持つクラスにできます。
一方、抽象クラスを新たに追加する場合、既存のクラスの継承階層を変更する必要があり、難易度が上がります。
ミックスインの実現
インタフェースは「ミックスイン」の定義に最適です。ミックスインとは、クラスが主な役割に該当する本来の型に加えて、別の振る舞いを提供できる仕組みです。
たとえば、Comparable
インタフェースは、オブジェクトの比較方法を定義するミックスインの一例です。
抽象クラスが適している場面
部分的な実装を共有したい場合
抽象クラスは、メソッドやフィールドを共有できます。
たとえば、動物クラスを継承する猫や犬のクラスに共通する振る舞いを定義できます。
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 抽象メソッド
public abstract void makeSound();
public void eat() {
System.out.println(name + " eats anything.");
}
}
デフォルトメソッドでは不十分な場合
インタフェースには、デフォルトメソッドという機能があります。
これによりインターフェースに共通メソッドを定義することができます。
しかし、デフォルトメソッドでは、equals
やhashCode
など、Objectクラスのメソッドをオーバーライドすることはできません。
一方、抽象クラスであれば、これらをオーバーライドして共通化できます。
インタフェースと抽象クラスを組み合わせる方法
骨格実装クラス(Skeletal Implementation)
Javaのコレクションフレームワークのように、インタフェースと抽象クラスを組み合わせる設計も可能です。
具体例として、AbstractList
はListインタフェースの骨格実装を提供し、開発者は最低限のコードでカスタムリストクラスを作成できます。
以下は、簡単な骨格実装の例です。
import java.util.AbstractList;
public class IntArrayAsList extends AbstractList<Integer> {
private final int[] array;
public IntArrayAsList(int[] array) {
this.array = array;
}
@Override
public Integer get(int index) {
return array[index];
}
@Override
public int size() {
return array.length;
}
}
このように骨格実装はインタフェースの基本メソッドを提供し、開発者が細かい実装に集中できる環境を整えます。
まとめ: インタフェースと抽象クラスの選び方
共通の仕様だけを提供したい場合: インタフェース
共通の実装も提供したい場合: 抽象クラス
既存のクラスを柔軟に拡張したい場合: インタフェース
一部のメソッドだけを具体的に実装したい場合: 骨格実装
最後に
今回はインターフェースと抽象クラスについて簡単に解説しました。
インタフェースと抽象クラスは、状況によって使い分けることで、コードの再利用性や保守性を高めることができます。それぞれの特徴を活かして、効率的な開発を目指しましょう!