KATOエンジニヤリング開発日誌

「アウトプット無きエンジニアにインプットもチャンスも無い」の精神で書いています

Javaのインターフェースについて学ぶ

「Javaプログラミング技法」の第12回目の授業内容まとめになります。

※前回授業の内容はこちら

www.kato-eng.info

インターフェース

インターフェースとは抽象クラスと似た仕組みで抽象メソッドやフィールドを宣言することができる。但し抽象クラスと異なり具象メソッドの実装はできない。他にも以下の特徴がある。

  • インターフェースを実装(継承と似た概念)したクラスに抽象メソッドのオーバーライドを強制できる
  • インターフェースを実装したクラスのインスタンスはインターフェース型の変数に代入することができる
  • フィールドは定数として扱われる(public static final)
  • メソッドは抽象メソッドとして扱われる(public abstract)
  • コンストラクタを持たない

Java7まではインターフェースで定義するメソッドは抽象メソッドで処理を実装することはできなかった。Java8からは「default」キーワードを付けると処理を書けるようになった。default実装を持つインターフェースを実装するクラスには、メソッドの処理が継承される。

インターフェースの利用例

インターフェースの宣言は以下のようにする。

interface インターフェース名 {
    型名 フィールド名 = 式;
    戻り値の型 メソッド名(引数);
}

インターフェースを利用する際の例は下記の通り。

// ISomething.java

package jpt12;

interface ISomething {
    // フィールドは public static final として扱われる
    int num = 100;
    void say();
}
// YourSub.java

package jpt12;

class YourSub implements ISomething {
    private int a;

    public YourSub(int a) {
        this.a = a;
    }

    // インターフェースで定義した抽象メソッドである「say」をオーバーライドしないとコンパイルエラーになる
    @Override
    public void say() {
        System.out.println("Hello, YourSub!!");
        return;
    }

    public int getA() {
        return a;
    }
}
// Ex01.java

package jpt12;

public class Ex01 {
    public static void main() {
        // インターフェースから継承したフィールドは static final なのでインスタンス化せずにアクセスできる
        System.out.println(YourSub.num);

        // YourSub型として扱うとYourSub独自のメンバと
        // インターフェースのISomethingから受け継いだメンバを利用できる
        YourSub ys = new YourSub(10);
        ys.say();
        System.out.println(ys.getA());

        // YourSubクラスのインスタンスをISomething型で宣言することができる
        // ISomething型として扱うとISomethingが提供するメンバしか扱えない
        ISomething is = new YourSub(10);
        is.say();
        //System.out.println(is.getA())
    }
}

出力結果は以下の通り。

100
Hello, YourSub!!
10
Hello, YourSub!!

インターフェースを実装したクラスインスタンス

上記の例ではインターフェース型の変数「is」にYourSubのインスタンスを代入している。図示すると以下のように考えられる。

f:id:masayuki_kato:20170726231434p:plain

高度なインターフェースの利用

抽象クラスは継承する側のクラスは1つしか継承できないという制約がある(単一継承)。しかしインターフェースは実装する側のクラスで複数のインターフェースを実装(継承)することができる。また、抽象クラスを継承すると同時にインターフェースを実装(継承)することもできる。これらを多重継承という。

他にも、インターフェースはさらに上位の継承し、インターフェースを拡張するといったことができる。

複数インターフェースの実装

「MyClass」クラスが「MyIntA」と「MyIntB」インターフェースを実装したい場合は下記のように記述する。

class MyClass implements MyIntA, MyIntB {
    // MyIntAとMyIntBで宣言されているすべての抽象メソッドをオーバーライドする
    // MyClassインスタンスはMyIntA、MyIntBのどちらの型の変数にも代入することができる
}

継承と実装を同時に行う

サブクラスでスーパークラスの継承とインターフェースの実装を同時に行うことができる。

Class UniversityStudent extends Student implements Work {
    // Studentクラスからメンバを受け継ぐ
    // StudentとWorkからすべての抽象メソッドをオーバーライドする
}

インターフェースの拡張

既存のインターフェースを拡張して新しいインターフェースを作成する。スーパーインターフェースを拡張する際には「implements」ではなく「extends」キーワードを使うことに注意する。

interface サブインターフェース名 extends スーパーインターフェース名1, スーパーインターフェース名2 {
    // サブインターフェースにはすべてのスーパーインターフェースのメソッドや定数が受け継がれる
}

多重継承

あるサブクラスが複数のクラスを継承することを多重継承という。多重継承には継承関係が複雑になるという問題がある。

下図は単一継承を表している。単一継承は木構造のクラス関係になる。

f:id:masayuki_kato:20170726234604j:plain

下図は多重継承を表している。多重継承はネットワーク構造のクラス関係になる。その結果、同一クラスを継承してしまうことがある。

f:id:masayuki_kato:20170726235900j:plain

Javaは多重継承が認められていないため多重継承特有の問題は発生しないが、1つのクラスしか継承できないと不便ではある。インターフェースはインスタンスをつくることができないので同一クラスを継承することは無い。

抽象クラスとインターフェースの使い分け

  • 実装(処理、中身)を持ったメソッドをサブクラスへ継承させたい場合は抽象クラスを使用する
    • インターフェースではオーバーライドしたメソッドの処理をすべて書き直す必要があるため
  • インスタンス毎に状態(フィールド)を持たせたい場合は抽象クラスを使用する
    • インターフェースのフィールドは public static な定数であり、インスタンス変数ではないため
  • すでに別クラスを継承しているクラスを拡張したい場合はインターフェースを使用する
    • 複数のクラスを同時に継承することはできないため