Java道|インターフェイスで実現する多重継承

親クラスは1つだけでも、守る約束は複数持てる。
インターフェイスを使うと、Javaでは多重継承の考え方を安全で分かりやすい形にできます。

ここまで、クラスの継承、抽象クラス、インターフェイスを順番に学んできました。

継承を使うと、親クラスの機能を子クラスへ受け継がせることができます。
抽象クラスを使うと、共通の土台を作りながら、未完成の処理を子クラスに任せることができます。
インターフェイスを使うと、クラスに「このメソッドを必ず持つ」という共通の約束を持たせることができます。

ここで次に気になるのが、
1つのクラスに複数の性質や約束を持たせたい場合はどうするのか
という点です。

鬼滅の刃風にたとえると、1人の和風剣士は、1つの特徴だけで成り立っているわけではありません。

たとえば、ある剣士は次のような複数の性質を持っているかもしれません。

性質鬼滅の刃風のイメージ
剣士である戦闘できる
情報を表示できる自分の名前や戦闘力を示せる
防具を持つ羽織や鎧の情報を示せる
特殊能力を使える呼吸や支援技を使える

しかし、Javaでは、1つのクラスが複数のスーパークラスを同時に継承することはできません。
つまり、クラスそのものの多重継承は認められていません。

そこで登場するのが インターフェイスの複数実装 です。

Javaでは、クラスの親クラスは1つだけですが、インターフェイスは複数実装できます。
これにより、1つのクラスに複数の「約束」や「能力のルール」を持たせられます。

鬼滅の刃風にたとえると、
「この剣士は、戦士として情報を示す約束も守るし、防具情報を示す約束も守る」
と宣言できるようなものです。

Javaではクラスの多重継承はできない

まず、出発点をはっきりさせましょう。

Javaでは、1つのクラスが2つ以上のスーパークラスを同時に継承することはできません。

たとえば、次のような2つのクラスがあるとします。

class Slayer
{
}

class ArmorUser
{
}

この2つを同時に親クラスとして持つように、次のような書き方はできません。

// これはできない
class WaterPillar extends Slayer, ArmorUser
{
}

Javaのクラス継承では、extends のあとに指定できる親クラスは1つだけです。

書き方Javaで可能か理由
class WaterPillar extends Slayer可能親クラスが1つだけ
class WaterPillar extends Slayer, ArmorUser不可能親クラスを複数指定している

鬼滅の刃風にたとえると、1人の剣士クラスが「剣士の基本クラス」と「防具使いの基本クラス」を、同時に親として持つことはできないということです。

Javaは、クラスの親子関係を1本の系譜として整理します。
そのため、クラスの継承では多重継承を禁止しています。

なぜクラスの多重継承はできないのか

クラスの多重継承ができると、一見とても便利そうに見えます。

しかし、複数の親クラスを同時に持つと、設計が複雑になりやすいです。

たとえば、2つの親クラスが同じ名前のメソッドを持っていたらどうでしょうか。

class Slayer
{
    void show()
    {
        System.out.println("剣士の情報を表示します。");
    }
}

class ArmorUser
{
    void show()
    {
        System.out.println("防具使いの情報を表示します。");
    }
}

もし、1つのクラスがこの2つを同時に継承できた場合、show() を呼び出したときに、どちらの show() を使うのか分かりにくくなります。

起こりやすい問題内容
同名メソッドの衝突どちらの親のメソッドを使うか迷う
同名フィールドの衝突どちらの値を指すのか分かりにくい
初期化順序が複雑どの親クラスを先に初期化するのか迷う
設計が読みにくいクラスの責任範囲があいまいになる

鬼滅の刃風にたとえると、1人の剣士が2つの流派の基本型を同時に親として持ち、両方に同じ名前の奥義がある状態です。

そのとき、同じ奥義名を呼んだら、どちらの流派の奥義が出るのか分かりにくくなります。

Javaは、このような混乱を避けるために、クラスの親は1つだけというルールにしています。

↓クリックすると拡大表示されます。

それでも複数の性質を持たせたい場面はある

ただし、現実の設計では、1つのクラスが複数の性質や能力を持つことは自然です。

鬼滅の刃風に考えると、ある剣士は次のような複数の特徴を持っているかもしれません。

持たせたい性質
剣士として情報を表示できる名前や戦闘力を表示する
防具情報を表示できる羽織や鎧の情報を表示する
飛行できる空中移動できる
支援できる仲間を回復・補助できる

もしクラスの継承だけに頼ると、これらを柔軟に組み合わせるのが難しくなります。

そこで Java は、クラスの多重継承は許さない代わりに、インターフェイスの複数実装 を用意しています。

つまり、Javaは次のように役割を分けています。

しくみ役割
クラスの継承親クラスは1つだけにして、構造を整理する
インターフェイスの実装複数の能力や約束を持たせる

このように、Javaは分かりやすさと柔軟さを両立しています。

インターフェイスの複数実装とは何か

Javaでは、1つのクラスが2つ以上のインターフェイスを実装できます。

このときは、implements のあとにインターフェイス名をカンマで並べます。

基本の形は次のとおりです。

class クラス名 implements インターフェイス名1, インターフェイス名2
{
}

たとえば、鬼滅の刃風に、次の2つのインターフェイスがあるとします。

インターフェイス役割
iSlayer剣士としての情報を表示する約束
iArmor防具情報を表示する約束

この2つを、1つの WaterPillar クラスが実装できます。

class WaterPillar implements iSlayer, iArmor
{
}

これは、WaterPillar クラスが次の2つの約束を同時に引き受けるという意味です。

引き受ける約束必要なメソッド
iSlayer の約束sShow()
iArmor の約束aShow()

鬼滅の刃風にたとえると、WaterPillar は、
「剣士として情報を示せる」
「防具の情報も示せる」
という2つの役割を同時に持つことになります。

なぜこれが多重継承の一部と言えるのか

ここで大切なのは、インターフェイスの複数実装は、クラスそのものを複数継承しているわけではないという点です。

複数の親クラスからフィールドや具体的な処理を受け継ぐのではありません。

受け継ぐのは、主に メソッド名の約束 です。

たとえば、次のような2つのインターフェイスがあるとします。

interface iSlayer
{
    void sShow();
}

interface iArmor
{
    void aShow();
}

この2つを実装するクラスは、sShow() と aShow() の両方を定義しなければなりません。

class WaterPillar implements iSlayer, iArmor
{
    public void sShow()
    {
    }

    public void aShow()
    {
    }
}

つまり、複数のインターフェイスが定めたメソッド名のルールを、1つのクラスがまとめて引き受けているわけです。

多重継承の種類Javaでの扱い
複数のクラスを同時に継承するできない
複数のインターフェイスを同時に実装するできる
複数の約束を1つのクラスに持たせるできる

鬼滅の刃風にたとえると、1人の剣士が複数の親を持つのではありません。
複数の任務規則や能力の約束を同時に守る、というイメージです。

だから、インターフェイスの複数実装は、Javaで多重継承の考え方の一部を安全に実現する方法だと考えられます。

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

ファイル名:Sample4.java

interface iSlayer
{
    void sShow();
}

interface iArmor
{
    void aShow();
}

class WaterPillar implements iSlayer, iArmor
{
    private String name;
    private int swordPower;

    public WaterPillar(String n, int sp)
    {
        name = n;
        swordPower = sp;
        System.out.println(name + " 剣力" + swordPower + "の水柱剣士を作成しました。");
    }

    public void sShow()
    {
        System.out.println("水柱剣士の名前は" + name + "です。");
        System.out.println("剣力は" + swordPower + "です。");
    }

    public void aShow()
    {
        System.out.println("水柱剣士の防具は青紋の羽織です。");
    }
}

class Sample4
{
    public static void main(String[] args)
    {
        WaterPillar slayer1 = new WaterPillar("水月", 18000);
        slayer1.sShow();
        slayer1.aShow();
    }
}

実行結果

水月 剣力18000の水柱剣士を作成しました。
水柱剣士の名前は水月です。
剣力は18000です。
水柱剣士の防具は青紋の羽織です。

WaterPillar が2つのインターフェイスを実装している

このプログラムでは、WaterPillar クラスが2つのインターフェイスを実装しています。

class WaterPillar implements iSlayer, iArmor

ここで宣言しているのは、次の2つです。

実装しているもの意味
iSlayer剣士としての情報表示の約束を守る
iArmor防具情報表示の約束を守る

つまり WaterPillar は、iSlayer の約束も、iArmor の約束も引き受けています。

そのため、WaterPillar クラスでは、両方のインターフェイスが要求するメソッドを定義する必要があります。

インターフェイス要求するメソッドWaterPillar で定義する必要
iSlayersShow()必要
iArmoraShow()必要

このように、インターフェイスを複数実装するということは、複数の約束を同時に引き受けることです。

鬼滅の刃風にたとえると、水柱剣士が「剣士として名乗る役割」と「防具情報を示す役割」の両方を持つということです。

sShow() と aShow() は何を表しているのか

この例では、2つのインターフェイスが別々の役割を持っています。

interface iSlayer
{
    void sShow();
}

iSlayer は、剣士としての情報を表示する約束です。

interface iArmor
{
    void aShow();
}

iArmor は、防具情報を表示する約束です。

WaterPillar クラスでは、この2つを次のように実装しています。

public void sShow()
{
    System.out.println("水柱剣士の名前は" + name + "です。");
    System.out.println("剣力は" + swordPower + "です。");
}

sShow() では、剣士としての名前と剣力を表示しています。

public void aShow()
{
    System.out.println("水柱剣士の防具は青紋の羽織です。");
}

aShow() では、防具として青紋の羽織を表示しています。

整理すると、次のようになります。

メソッド役割表示内容
sShow()剣士としての情報表示名前、剣力
aShow()防具情報の表示青紋の羽織

同じ WaterPillar オブジェクトが、2つの異なる役割を持っていることが分かります。

なぜクラスの多重継承より扱いやすいのか

クラスの多重継承では、複数の親クラスから具体的なフィールドやメソッドを受け継ぐことになります。
そのため、同じ名前のメンバがあったときに衝突しやすくなります。

一方、インターフェイスは、主に「こういうメソッドを持つこと」という約束を表します。
そのため、役割を分けやすく、クラス構造も複雑になりにくいです。

しくみ役割
クラスの継承共通の土台を受け継ぐ
インターフェイス能力や約束を追加する

鬼滅の刃風にたとえると、クラスは「水柱剣士という存在そのもの」を表します。
インターフェイスは「剣士として情報を表示できる」「防具情報を表示できる」という能力ルールを表します。

つまり、クラスで「何者か」を表し、インターフェイスで「何ができるか」を追加しているわけです。

この整理があるので、Javaではクラスの多重継承を使わなくても、複数の能力を自然に表現できます。

インターフェイスの複数実装が便利な場面

インターフェイスの複数実装は、1つのクラスに複数の役割を持たせたいときに便利です。

鬼滅の刃風に考えると、次のような使い方ができます。

インターフェイス役割
iSlayer剣士として情報を表示できる
iArmor防具情報を表示できる
iFlyable空中移動できる
iSupporter仲間を支援できる

たとえば、将来的に WaterPillar に飛行能力の約束も追加したい場合は、次のようにできます。

class WaterPillar implements iSlayer, iArmor, iFlyable
{
}

このように、親クラスを増やさなくても、必要な能力の約束を増やせます。

目的インターフェイスでの表現
情報表示できることを保証したいiSlayer
防具情報を表示できることを保証したいiArmor
飛行できることを保証したいiFlyable
支援できることを保証したいiSupporter

これにより、クラスは1つの基本構造を保ちながら、能力や役割の面では柔軟に広げられます。

抽象クラスとの違いも意識しておきたい

インターフェイスの複数実装を理解するには、抽象クラスとの違いも押さえておくと分かりやすいです。

比較項目抽象クラスインターフェイス
役割共通の土台共通の約束
通常フィールド持てる原則として持てない
通常メソッド持てる原則として処理を書かない
コンストラクタ持てる持てない
継承・実装extends で1つだけimplements で複数可
表しやすいもの何者か何ができるか

鬼滅の刃風にたとえると、抽象クラスは「剣士としての共通の土台」です。
たとえば、speed や setSpeed() のような共通状態・共通処理を持てます。

一方、インターフェイスは「能力や役割の約束」です。
たとえば、情報を表示できる、防具情報を表示できる、飛べる、支援できる、といった約束を表します。

使いたい場面向いているもの
共通の状態や処理を持たせたい抽象クラス
複数の能力や約束を持たせたいインターフェイス
1つの親として土台を作りたい抽象クラス
複数の役割を同時に表したいインターフェイス

この違いを意識すると、インターフェイスの複数実装が、なぜ多重継承の考え方につながるのかが見えてきます。

オブジェクト指向らしい自然な設計につながる

オブジェクト指向では、クラスを「何者か」で整理するだけでなく、「何ができるか」で整理することも大切です。

WaterPillar は、水柱剣士という具体的な存在です。

しかし、それだけでなく、次のような能力や役割を持っています。

見方内容
何者かWaterPillar
何ができるかsShow() で剣士情報を表示できる
何ができるかaShow() で防具情報を表示できる

この「何ができるか」を表すのがインターフェイスです。

インターフェイスを使うと、現実の考え方に近い形でクラスを整理できます。

鬼滅の刃風にたとえると、1人の剣士は「水柱である」という存在としての分類を持ちながら、
「情報を表示できる」
「防具情報を示せる」
「仲間を支援できる」
のような複数の役割も持てます。

Javaでは、この複数の役割をインターフェイスで表現できます。

図:2つのインターフェイスを1つのクラスが実装する

↓クリックすると拡大表示されます。

この図が示していること

この図では、iSlayer と iArmor という2つのインターフェイスから、WaterPillar クラスへ約束が集まる様子を表しています。

iSlayer は sShow() という約束を持っています。
iArmor は aShow() という約束を持っています。

WaterPillar は、その両方を implements で実装しています。

インターフェイス約束WaterPillar の責任
iSlayersShow() を持つsShow() を定義する
iArmoraShow() を持つaShow() を定義する

この図から分かることは、Javaではクラスの多重継承はできなくても、インターフェイスを使えば複数のメソッドの約束を1つのクラスに集められるということです。

図:クラス継承とインターフェイス複数実装の違い

↓クリックすると拡大表示されます。

この図が示していること

この図では、クラス継承とインターフェイス複数実装の違いを比較しています。

左側では、クラス継承は親クラスを1つだけ持てることを表しています。
複数の親クラスから1つの子クラスへ継承する形にはバツ印が付いています。

右側では、WaterPillar が iSlayer と iArmor の2つのインターフェイスを実装しています。
これは、複数の親クラスを持っているのではなく、複数の約束を引き受けている状態です。

しくみできること
extends親クラスを1つだけ指定できる
implements複数のインターフェイスを指定できる

この図から分かることは、Javaはクラスの構造を1本に保ちながら、インターフェイスによって複数の能力や役割を表現しているということです。

鬼滅の刃風にインターフェイスの多重実装を整理する

Javaでは、1つのクラスが複数の親クラスを持つことはできません。
つまり、クラスの多重継承はできません。

しかし、1つのクラスが複数のインターフェイスを実装することはできます。

鬼滅の刃風にたとえると、1人の剣士が複数の親を持つのではありません。
その代わりに、複数の能力や役割の約束を持つことができます。

Javaのしくみ鬼滅の刃風のイメージ
クラスの親は1つ剣士の系譜は1本
インターフェイスは複数実装できる複数の任務規則や能力を持てる
iSlayer剣士情報を表示する約束
iArmor防具情報を表示する約束
WaterPillar両方の約束を守る具体的な剣士

つまり、インターフェイスによる多重実装は、
親クラスを増やすのではなく、守るべき約束を増やすことで、多重継承の考え方を表現する仕組み
です。

この感覚がつかめると、Javaがなぜクラスの多重継承を認めず、それでもインターフェイスで柔軟さを残しているのかが自然に見えてきます。

この内容で押さえておきたいポイント

ポイント内容
Javaのクラス継承親クラスは1つだけ
クラスの多重継承Javaではできない
インターフェイスの複数実装1つのクラスで複数指定できる
書き方implements インターフェイス名1, インターフェイス名2
実装クラスの責任すべてのインターフェイスのメソッドを定義する
インターフェイスの役割能力や約束を表す
抽象クラスとの違い抽象クラスは共通の土台、インターフェイスは共通の約束
多重継承の考え方複数の約束を同時に引き受ける形で一部を実現する

インターフェイスの複数実装を使うと、1つのクラスに複数の役割を持たせられます。

WaterPillar は、iSlayer の約束も iArmor の約束も守ります。
そのため、sShow() で剣士情報を表示でき、aShow() で防具情報も表示できます。

Javaでは親クラスを複数持つことはできません。
しかし、インターフェイスを使えば、複数の能力や約束を安全に組み合わせることができます。