talosのプログラミング教室

はじめてのJava ~抽象クラスとインタフェース編~

スポンサーリンク


今回はJava抽象クラスインタフェースについて説明します。

この2つはとても似ている機能なので、まとめて紹介します。

初心者が混同しやすいこの2つですが、使える場面が異なります。

抽象クラスとインタフェースはなぜ必要なのか

前回、継承について説明したときは、継承することによってコードを効率的に再利用できると言いました。

talosta.hatenablog.com

しかし、これが別のプロジェクトになったらどうなるでしょうか?

メソッドの名前が同じでも中身が同じとは限りません。


例を挙げて考えてみましょう。

とある企業Aは3つのプロジェクトを同時進行しています。

その3つのプロジェクトはコードの再利用が可能であることがわかりました。

優秀なプログラマBさんは、いずれでも使うと思われるClass1クラスを作っておくことにしました。

Class1クラスではとある計算をするcalc()メソッドを定義します。

しかし、詳細は未定であるためメソッドの中身までは実装できません。

そこでメソッドの中身を空にしておくことにしました。

Bさんはこのクラス継承されることを想定しています。

class Class1 {
    void calc() {
    }
}

数日後、それぞれのプロジェクトが進行し、どちらも開発フェーズに入りました。


プロジェクト1にアサインされている適当プログラマCさんはClass1 のコードを見てこう思います。

「このメソッドはなにもしないメソッドなんだ」

詳細が決定してから実装するはずであった箇所を、Cさんは勘違いして無視してしまいました。


また、プロジェクト2にアサインされている多忙プログラマDさんは設計書をよく読んだので、Class2クラスでClass1クラスを継承し、calc()メソッドをオーバーライドしなくてはいけないことを知っています。

しかし、疲れからかオーバーライドするのを忘れてしまいました。


さらにプロジェクト3にアサインされている新人プログラマEさんは、Class1クラスを以下のように使いました。

class Main1 {
    public static void main(String args[]) {
        Class1 c1 = new Class1();
        // 以下省略
    }
}

いまいち継承の使い方がわからず、インスタンスを作ってしまいました。


これらのような失敗を起こさないため、抽象クラスインタフェースを使います。

それでは、それぞれの使い方を見ていきます。

抽象クラスの使い方

abstract class Animal {
    abstract void walk();
}

classの前に「abstract」を付けます。

試しにオーバーライドをせずにコンパイルしてみましょう。

class Dog extends Animal {
    void bark() {
        System.out.println("ワン");
    }
}
// エラーメッセージ
Dog.java:1: エラー: Dogはabstractでなく、Animal内のabstractメソ 
ッドwalk()をオーバーライドしません
class Dog extends Animal {
^
エラー1個

しっかりエラーが出ました。

これでCさんとDさんの失敗は解決できたことがわかります。


次に、Animalクラスをインスタンス化してみます。

class AnimalMain {
    public static void main(String args[]) {
        Animal dog = new Animal();
    }
}
// エラーメッセージ
AnimalMain.java:3: エラー: Animalはabstractです。インスタンスを 
生成することはできません
        Animal dog = new Animal();
                     ^
エラー1個

こちらもエラーになり、Eさんの失敗は解決しました。

インタフェースの使い方

では、インタフェースはどのようなときに使うのでしょうか?

抽象クラスに対するインタフェースの特徴は多重継承できる点です。

仮にクラス多重継承ができるとしましょう。

クラスの多重継承

WolfクラスとHumanクラスを継承した狼人間はどのように動くのでしょうか?

両方から異なる動作のmove()メソッドを継承するため、不具合が起きてしまいます。

一方で、インターフェース多重継承はどうでしょうか。

インタフェースの多重継承

オーバーライドによって独自のmove()メソッドを実装することができます。

これで多重継承の問題は解決です。


それではインタフェースの使い方を見ていきましょう。

interface Wolf {
    void move();
}
interface Human {
    void move();
}
class WolfMan implements Wolf, Human {
    public void move() {
        System.out.println("walk:bipedal, run:quadruped");
    }
}
class WolfManMain {
    public static void main(String args[]) {
        WolfMan wm = new WolfMan();
        wm.move();
    }
}
// 出力
walk:bipedal, run:quadruped

インタフェースには「interface」を付け、実装側では「implements」で使用するインタフェースを明示します。

おわりに

今回はJava抽象クラスインタフェースについて説明しました。

次回はパッケージについて説明します。