Java
2022.01.07
Javaのinterfaceとは?インターフェースの使い方やabstractクラスとの違い
2023.11.15

Javaを学習する上で、ひとつの難関ポイントと言えるのが「オブジェクト指向」、そして「interface(インターフェイス)」です。

インターフェイスの基本的な知識や、どのような場面で使うのかを詳しく説明していきます。


1. Javaのinterface(インターフェイス)とは?

interface(インターフェイス)は、クラスがどのようなメソッドを持っているのかをあらかじめ定義する、いわば設計書のような存在です。


クラスとは異なり、具体的な処理内容を記述せず、メソッドの引数や戻り値だけを定義します。

これにより、定義したインターフェイスをもとに、あとからインターフェイスの内容に応じたメソッドの処理をクラスとして実装することが可能になるのです。

2. interfaceを使うメリット


インターフェイスを使うことのメリットを紹介します。

①ポリモーフィズムを実現できる

インターフェイスを利用することで、オブジェクト指向における「ポリモーフィズム」(多様性)を実現できます。


ポリモーフィズムは、オブジェクト指向言語における重要な概念のひとつで、複数のオブジェクトが同じ命令に対して違う挙動を行うことを可能とします。


たとえば、「生物」というオブジェクトがあったとき、


・犬は「ワン」と鳴く

・猫は「にゃー」と鳴く

・ヒヨコは「ピヨピヨ」と鳴く


といったように、「鳴く」という命令に対して、それぞれで違う鳴き声を発します。


この時、「鳴く」という共通の動作の入り口(インターフェイス)を定義することがインターフェイスの役割です。

②実装漏れを防げる

インターフェイスをうまく活用することで、大規模開発の際の実装漏れを防げます。


インターフェイスは、そのインターフェイス内で定義したメソッドの実装を保証します。インターフェイスで定義された抽象メソッドが未実装の場合、コンパイルエラーが発生するのです。


よって、あらかじめ必要なメソッドをインターフェイスに定義しておけば、実装漏れによるバグの発生を防げます。

③多重継承できる

インターフェイスには、多重継承という概念があります。


例として、生物をひとつ掘り下げて「哺乳類」、「鳥類」、「爬虫類」という3つのインターフェイスを考えてみましょう。


・哺乳類は母乳で子供を育てる

・鳥類はクチバシを持つ

・鳥類や爬虫類は卵を産む


さて、「カモノハシ」という動物をご存知でしょうか。
このカモノハシは「地球上で最も奇妙な動物」と言われています。


分類上は哺乳類なのですが、


・クチバシを持っている

・卵を産む

・母乳で子供を育てる


という、鳥類や爬虫類の遺伝子を持つ特殊な存在なのです。


そのため、カモノハシに相当するクラスを作る場合には、
哺乳類だけでなく鳥類や爬虫類のインターフェイスを実装することになります。


このように、複数のインターフェイスを実装することを「多重継承」と呼びます。


インターフェイスを利用することで、このような多重継承を実現できるのです。



3. interfaceの使いどころ


他のクラスから呼び出されるような共通クラスを開発する場合、インターフェイスに共通クラスのメソッドの情報をあらかじめ定義できます。


これにより、共通クラスが完成していなくても、その共通クラスを呼び出す処理を並行して開発できます。


例として、全員が利用する共通のクラスである「消費税率計算」のクラスを考えてみましょう。

本来であれば、この消費税率計算を行うクラスが完成するまで、このクラスを使うすべてのクラスは開発を進めることができません。


その場合、あらかじめ以下のようなインターフェイスを用意します。

/**
 * 消費税計算
 */
public interface TaxCalculator {
    /**
     * 消費税の計算
     * @param price 売価
     * @param date 基準日
     * @return 消費税額
     */
    double getTax(int price, Date date);

    /**
     * 消費税の計算
     * @param price 売価
     * @return 消費税額
     */
    double getTax(int price);
    /**
     * 税込価格の計算
     * @param price 売価
     * @param date 基準日
     * @return 税込価格
     */
    double getTotal(int price, Date date);

    /**
     * 税込価格の計算(当日付)
     * @param price 売価
     * @return 税込価格
     */
    double getTotal(int price);
}

こうすることで、このクラスを利用する開発者はインターフェイスの情報をもとに税率計算開発を並行して進めることができるのです。


このように、インターフェイスは個人で開発する場合にはあまりメリットが見えにくい機能ですが、複数人で開発する場合には大きなメリットになります。

4. interfaceの使い方

インターフェイスのメリットや使いどころを理解したところで、実際の定義方法や、便利な使い方を紹介していきます。

①interfaceの基本的な使い方

ここでは、インターフェイスの基本的な使い方を紹介します。


interfaceの定義方法

まずは、インターフェイスの記載ルールから紹介します。
基本の形は以下の通りです。

public interface インターフェイス名 {
    
    型 フィールド変数;
       
    戻り値 メソッド名(引数1, 引数2, ...);
}

原則として、インターフェイスには以下の2点のみが宣言できると覚えましょう。

  1. フィールド変数
  2. 抽象メソッド(引数と戻り値のみを記載)

この際、インターフェイスに定義した内容はすべてpublicとして扱われます。

また、フィールド変数はstatic finalが付与されるため、自動的に定数に変換されます。


例として、「鳴く」メソッドを定義した「生物」インターフェイスを作ってみましょう。

public interface Life {
    /** 鳴く */
    void cry();
}


interfaceの実装方法

interfaceを実装するには、作成したクラスに「implements」を用いてインターフェイスを指定します。

例として、生物インターフェイスを実装した犬クラスを作成してみましょう。

public class Dog implements Life {
    @Override
    public void cry() {
        System.out.println("ワン");
    }
}

Lifeインターフェイスに定義しているcryメソッドを実装します。


interfaceの継承方法

インターフェイスは、他のインターフェイスを継承して新しいインターフェイスを定義することが可能です。

例として、「車」というインターフェイスを継承した「貨物車」インターフェイスを紹介します。

/**
 * 車インターフェイス
 */
public interface Vehicle {
    /** アクセル */
    void accel();

    /** ブレーキ */
    void brake();
}

/**
 * 貨物車インターフェイス
 */
public interface CargoVehicle extends Vehicle{

    /** 積み込み */
    void loading(Object cargo);

    /** 荷下ろし */
    Object unloading();
}

こうすることで、「車インターフェイス」を拡張した「貨物車インターフェイス」を作成できます。


また、この貨物車インターフェイスには合計で4つのメソッドの実装をすることになります。


なお、インターフェイスをクラスに実装する場合には「implements」を使用していましたが、インターフェイスを継承する場合には「extends」を指定することに注意しましょう。

②defaultメソッドの使い方

原則として、インターフェイスには抽象メソッドのみを記載しますが、「default」を付与することで処理を実装することが可能です。

例として、先ほどの「生物」インターフェイスのcryメソッドをインターフェイスに実装してみます。

public interface Life {
    default void cry() {
        System.out.println("返事がない。鳴かない生物のようだ");
    }
}

このインターフェイスを実装する場合、cryメソッドを実装しない場合にはインターフェイスに定義したメソッドが実行されます。

public class Goldfish implements Life {
    // 何も実装しない
}
public static void main(String[] args) {
    GoldFish fish = new GoldFish();
    fish.cry();
}

これを実行すると、以下のように出力されます。

返事がない。鳴かない生物のようだ


このように、defaultでメソッドを定義することで、未実装メソッドに対するデフォルト(標準)の処理をインターフェイス上に定義できるのです。

③staticメソッドの使い方

staticメソッドは、defaultと同様にインターフェイス内に処理を記述するために利用します。

public interface Life {
    default void cry() {
        System.out.println("返事がない。鳴かない生物のようだ");
    }
    
    static String getExplain() {
        return "このクラスは生物を表します";
    }
}

staticメソッドはインスタンス化しなくても利用できるため、以下のような呼び出し方をします。

Life.getExplain();

このように、インターフェイスに固有の処理を追加したい場合にはstaticメソッドを使います。



5. interfaceとabstract(抽象)クラスの違いは?


インターフェイスと同様に、ポリモーフィズムを実現する機能として「abstract(抽象)クラス」が存在します。

ここでは、この抽象クラスとインターフェイスを比較します。

① abstract(抽象)クラスとは?

abstractクラスは、複数のクラスの共通処理をまとめた「スーパークラス」として定義を行うために利用します。

例として、先ほどのLifeをabstractクラスに書き換えてみます。

public abstract class Life {
    public abstract void cry();
}

この抽象クラスを継承してDogとCatを作成します。

public class Dog extends Life {
    @Override
    public void cry() {
        System.out.println("ワン");
    }
}

public class Cat extends Life {
    @Override
    public void cry() {
        System.out.println("にゃー");
    }
}

インターフェイスは「implements(実装)」と呼ぶのに対し、抽象クラスは「extends(継承)」を行います。


アクセス修飾子や「abstract」キーワードを付与したこと以外は、ほぼ同じような使い方が可能です。

②interfaceとabstractクラスの使い分け

インターフェイスと抽象クラスはできることに大きな違いがないため、
どちらを利用してもプログラム上は問題なく動作することがほとんどです。


ただし、以下のような違いがあります。

特徴interfaceabstract
アクセス修飾子publicのみprivate/protectedが使える
多重継承可能不可能


主に、抽象クラスは複数のクラスで使うような共通処理を記載するために利用します。

一方で、インターフェイスは外部から利用するためのメソッドを定義するような使い方をします。

そのため、


・ライブラリの外部へ公開する場合はインターフェイス

・ライブラリの内部で使用する場合は抽象クラス


を使うことが多いと覚えましょう。



この記事をシェア