Java道|インターフェイスの拡張と継承

能力の約束も、親から子へ受け継がれて強くなる。
インターフェイスの拡張を理解すると、Javaで「基本能力」と「発展能力」をきれいに整理できるようになります。

インターフェイスは、クラスのように「何者か」を表すものではなく、何ができるか を表す約束でした。

たとえば、鬼滅の刃風にたとえると、剣士にはさまざまな能力があります。

能力の約束鬼滅の刃風のイメージ
移動できる呼吸を使って素早く動ける
情報を表示できる自分の名前や能力を示せる
戦闘できる技を使って戦える
支援できる仲間を補助できる

Javaでは、このような「能力の約束」をインターフェイスとして表現できます。

そして大切なのは、インターフェイス同士にも親子関係を作れる という点です。
これを インターフェイスの拡張 といいます。

たとえば、まず基本の能力として「呼吸を使って動ける」という約束を作ります。
その上に、「呼吸を使って動けるうえに、剣士として情報を表示できる」という発展した約束を作れます。

鬼滅の刃風にたとえると、最初は「呼吸で動ける者」という基本の約束があり、そこから「呼吸で動けて、さらに剣士として名乗れる者」という上位の約束へ進化していくイメージです。

このように、インターフェイスを拡張すると、能力や役割を段階的に整理できます。

インターフェイスも拡張できる

Javaでは、クラスだけでなく、インターフェイスも拡張できます。

インターフェイスを拡張するときも、extends を使います。

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

interface サブインターフェイス名 extends スーパーインターフェイス名
{
}

ここで、拡張される側を スーパーインターフェイス、拡張する側を サブインターフェイス と考えます。

用語意味鬼滅の刃風のイメージ
スーパーインターフェイス基本となる能力の約束呼吸を使って動ける
サブインターフェイス基本の約束を受け継いだ発展版呼吸で動けて、剣士情報も示せる

サブインターフェイスは、スーパーインターフェイスの約束を受け継ぎます。
そのため、サブインターフェイスを実装するクラスは、サブインターフェイス自身のメソッドだけでなく、親インターフェイスから受け継いだメソッドも実装する必要があります。

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

鬼滅の刃風に考えるインターフェイスの拡張

まず、基本の能力として iBreathMovable というインターフェイスを考えます。

これは、
呼吸を使って動ける者
という約束です。

interface iBreathMovable
{
    void move();
}

この iBreathMovable は、move() を持つことを約束します。

次に、iBattleSlayer というインターフェイスを作ります。

これは、
呼吸を使って動けるうえに、剣士として情報を表示できる者
という発展した約束です。

interface iBattleSlayer extends iBreathMovable
{
    void show();
}

このように書くと、iBattleSlayer は iBreathMovable を拡張します。

つまり iBattleSlayer は、自分自身の show() だけでなく、親である iBreathMovable の move() も含むことになります。

インターフェイス持っている約束
iBreathMovablemove()
iBattleSlayermove()、show()

鬼滅の刃風にたとえると、iBreathMovable は「呼吸で移動できる」という基本能力です。
iBattleSlayer は、その能力を前提にして「剣士として情報も示せる」という追加能力を持つ上位ルールです。

スーパーインターフェイスとサブインターフェイスの関係

スーパーインターフェイスとサブインターフェイスの関係は、クラスの親子関係に少し似ています。

ただし、クラスの継承とは受け継ぐものが違います。

比較クラスの継承インターフェイスの拡張
受け継ぐものフィールドや具体的な処理メソッドの約束
表すもの何者か、共通の土台何ができるか、能力のルール
親の数基本的に1つ複数指定できる
使うキーワードextendsextends

クラスの継承は、たとえば Slayer クラスから WaterSlayer クラスを作るように、存在の土台を受け継ぎます。

一方、インターフェイスの拡張は、能力の約束を受け継ぎます。

鬼滅の刃風にたとえると、クラスの継承は「剣士という存在の系譜」です。
インターフェイスの拡張は「呼吸を使える」「移動できる」「戦闘情報を示せる」といった能力ルールの系譜です。

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

インターフェイスの拡張には extends を使う

インターフェイス同士の親子関係には extends を使います。

interface iBattleSlayer extends iBreathMovable
{
    void show();
}

このコードは、iBattleSlayer が iBreathMovable の約束を受け継ぐことを表しています。

ここで混同しやすいのが、extends と implements の違いです。

キーワード使う場面意味
extendsインターフェイスからインターフェイス親インターフェイスの約束を受け継ぐ
implementsクラスからインターフェイスクラスがその約束を実際に守る

つまり、インターフェイス同士では extends を使います。
クラスがインターフェイスを使うときは implements を使います。

鬼滅の刃風にたとえると、extends は「能力ルールが進化する」ことです。
implements は「実際の剣士クラスが、その能力ルールを引き受ける」ことです。

クラスがサブインターフェイスを実装するとどうなるか

ここがとても大切です。

クラスがサブインターフェイスを実装した場合、そのクラスはサブインターフェイス自身のメソッドだけでなく、スーパーインターフェイスから受け継いだメソッドもすべて定義しなければなりません。

たとえば、次の関係を考えます。

interface iBreathMovable
{
    void move();
}

interface iBattleSlayer extends iBreathMovable
{
    void show();
}

このとき、iBattleSlayer を実装するクラスは、show() だけでなく move() も実装する必要があります。

class WaterHashira implements iBattleSlayer
{
    public void move()
    {
    }

    public void show()
    {
    }
}

なぜなら、iBattleSlayer は iBreathMovable の約束を受け継いでいるからです。

実装するインターフェイス実装が必要なメソッド
iBattleSlayershow()
iBattleSlayer が受け継いだ iBreathMovablemove()

鬼滅の刃風にたとえると、WaterHashira が「戦闘剣士として名乗れる」と宣言するなら、前提となる「呼吸で動ける」能力も持っていなければならない、ということです。

インターフェイスの拡張が便利な理由

インターフェイスを拡張できると、能力の約束を段階的に整理できます。

すべての約束を1つのインターフェイスに詰め込むと、どこまでが基本能力で、どこからが発展能力なのか分かりにくくなります。

たとえば、次のような考え方です。

段階インターフェイス約束
基本能力iBreathMovable呼吸を使って動ける
発展能力iBattleSlayer呼吸で動けて、剣士情報も示せる
さらに発展iSupportSlayer呼吸で動けて、剣士情報を示し、支援もできる

このように分けると、能力の広がりがとても見やすくなります。

鬼滅の刃風にたとえると、最初は「呼吸を使える」、次に「呼吸を使って移動できる」、さらに「戦闘中に自分の情報や役割を示せる」と、修行で能力が段階的に増えていくイメージです。

インターフェイスを拡張する

ファイル名:Sample5.java

interface iBreathMovable
{
    void move();
}

interface iBattleSlayer extends iBreathMovable
{
    void show();
}

class WaterHashira implements iBattleSlayer
{
    private String name;
    private String breathingStyle;

    public WaterHashira(String n, String bs)
    {
        name = n;
        breathingStyle = bs;
        System.out.println(name + " 水の呼吸「" + breathingStyle + "」の柱剣士を作成しました。");
    }

    public void move()
    {
        System.out.println(name + "は呼吸を整え、霧の中を素早く移動します。");
    }

    public void show()
    {
        System.out.println("柱剣士の名前は" + name + "です。");
        System.out.println("呼吸の型は" + breathingStyle + "です。");
    }
}

class Sample5
{
    public static void main(String[] args)
    {
        WaterHashira slayer1 = new WaterHashira("水月", "水流斬り");

        slayer1.move();
        slayer1.show();
    }
}

実行結果

水月 水の呼吸「水流斬り」の柱剣士を作成しました。
水月は呼吸を整え、霧の中を素早く移動します。
柱剣士の名前は水月です。
呼吸の型は水流斬りです。

iBreathMovable は基本能力の約束を表す

このプログラムでは、まず iBreathMovable というインターフェイスを用意しています。

interface iBreathMovable
{
    void move();
}

これは、呼吸を使って動ける者の約束です。

iBreathMovable を実装するクラスは、move() を定義する必要があります。

インターフェイス約束
iBreathMovablemove() を持つ

鬼滅の刃風にたとえると、これは「呼吸を使って素早く動ける者」という基本能力の札です。

ただし、このインターフェイスだけでは、剣士情報を表示する約束までは持っていません。
あくまで、移動能力に関する基本の約束です。

iBattleSlayer は iBreathMovable を拡張している

次に、iBattleSlayer を見てみましょう。

interface iBattleSlayer extends iBreathMovable
{
    void show();
}

ここでは、iBattleSlayer が iBreathMovable を extends しています。

つまり iBattleSlayer は、iBreathMovable の move() を受け継ぎます。
さらに、自分自身の約束として show() を追加しています。

インターフェイス自分の約束受け継いだ約束合計で持つ約束
iBreathMovablemove()なしmove()
iBattleSlayershow()move()move()、show()

鬼滅の刃風にたとえると、iBattleSlayer は「呼吸で動ける」ことを前提にして、さらに「柱剣士として情報を示せる」という発展した能力ルールです。

このように、サブインターフェイスは親の約束を含んだ上で、新しい約束を追加できます。

WaterHashira は iBattleSlayer の約束を実装している

WaterHashira クラスは、iBattleSlayer を implements しています。

class WaterHashira implements iBattleSlayer

ここで注意したいのは、WaterHashira が直接 implements しているのは iBattleSlayer だけだという点です。

しかし、iBattleSlayer は iBreathMovable を拡張しています。

そのため、WaterHashira は show() だけでなく、move() も定義する必要があります。

public void move()
{
    System.out.println(name + "は呼吸を整え、霧の中を素早く移動します。");
}

public void show()
{
    System.out.println("柱剣士の名前は" + name + "です。");
    System.out.println("呼吸の型は" + breathingStyle + "です。");
}

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

クラス実装しているインターフェイス必要なメソッド
WaterHashiraiBattleSlayermove()、show()

鬼滅の刃風にたとえると、WaterHashira は「戦闘剣士として情報を示せる」という上位の約束を引き受けています。
そのため、前提となる「呼吸で動ける」という基本能力も持っていなければなりません。

move() と show() が表している役割

このプログラムでは、move() と show() が別々の役割を持っています。

メソッド由来役割
move()iBreathMovable呼吸を使って動く
show()iBattleSlayer剣士情報を表示する

move() は、iBreathMovable から来た基本能力です。

public void move()
{
    System.out.println(name + "は呼吸を整え、霧の中を素早く移動します。");
}

show() は、iBattleSlayer が追加した発展能力です。

public void show()
{
    System.out.println("柱剣士の名前は" + name + "です。");
    System.out.println("呼吸の型は" + breathingStyle + "です。");
}

この2つを WaterHashira が両方実装しているため、WaterHashira オブジェクトは「動ける」し「情報を示せる」存在になります。

extends と implements を混同しない

インターフェイスの拡張では extends を使います。
クラスがインターフェイスを実装するときは implements を使います。

ここはとても大切です。

書き方意味
interface iBattleSlayer extends iBreathMovableインターフェイスが親インターフェイスの約束を受け継ぐ
class WaterHashira implements iBattleSlayerクラスがインターフェイスの約束を実際に守る

鬼滅の刃風にたとえると、extends は「能力ルールどうしの進化」です。
implements は「実際の剣士がその能力ルールを身につけること」です。

この違いを押さえると、インターフェイスの階層がかなり分かりやすくなります。

クラスの継承との違い

クラスの継承とインターフェイスの拡張は、どちらも extends を使うため、少し似て見えます。

しかし、意味は違います。

比較項目クラスの継承インターフェイスの拡張
表すもの何者か、共通の土台何ができるか、能力の約束
受け継ぐものフィールドやメソッドの実装メソッドの約束
親の数1つだけ複数可
class WaterHashira extends Slayerinterface iBattleSlayer extends iBreathMovable

クラスの継承は、「水柱剣士は剣士の一種である」という関係を表すのに向いています。

インターフェイスの拡張は、「戦闘剣士は、呼吸で動ける能力を前提に、さらに情報表示もできる」という能力ルールの進化を表すのに向いています。

鬼滅の刃風にたとえると、クラスの継承は血筋や所属の系譜です。
インターフェイスの拡張は、修行によって能力ルールが上位化していく系譜です。

インターフェイスは複数のスーパーインターフェイスを拡張できる

クラスの継承では、親クラスは1つだけです。

一方で、インターフェイスは複数のスーパーインターフェイスを拡張できます。

形は次のようになります。

interface サブインターフェイス名 extends スーパーインターフェイス名1, スーパーインターフェイス名2
{
}

たとえば、鬼滅の刃風に考えるなら、次のような設計もできます。

interface iAdvancedSlayer extends iBreathMovable, iSupportable
{
    void show();
}

この場合、iAdvancedSlayer は iBreathMovable と iSupportable の両方の約束を受け継ぎます。

インターフェイス受け継ぐ約束
iBreathMovablemove()
iSupportablesupport()
iAdvancedSlayermove()、support()、show()

つまり、インターフェイスの拡張では、能力の約束を複数方向から集めて、さらに上位の能力ルールを作れます。

図:インターフェイスの拡張で約束を受け継ぐ

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

この図が示していること

この図では、iBreathMovable から iBattleSlayer へ約束が受け継がれ、その後 WaterHashira クラスがそれを実装する流れを表しています。

iBreathMovable は move() という基本の約束を持っています。
iBattleSlayer は iBreathMovable を extends しているため、move() を受け継ぎます。

さらに、iBattleSlayer は show() という新しい約束を追加しています。

そのため、WaterHashira が iBattleSlayer を implements する場合、show() だけでなく、move() も実装しなければなりません。

流れ内容
iBreathMovablemove() の約束を持つ
iBattleSlayermove() を受け継ぎ、show() を追加する
WaterHashiramove() と show() を実装する

この図から分かることは、インターフェイスの拡張では、親の約束がサブインターフェイスに受け継がれ、その約束が実装クラスにも届くということです。

図:クラスの継承とインターフェイスの拡張の違い

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

この図が示していること

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

左側では、WaterHashira が Slayer を extends しています。
これは「WaterHashira は Slayer の一種である」という型の系譜を表します。

右側では、iBattleSlayer が iBreathMovable を extends しています。
これは「iBattleSlayer は iBreathMovable の能力の約束を受け継ぐ」という能力ルールの系譜を表します。

比較クラスの継承インターフェイスの拡張
表すもの何者か何ができるか
受け継ぐもの状態や処理メソッドの約束
キーワードextendsextends

この図から分かることは、同じ extends を使っていても、クラスとインターフェイスでは受け継ぐものの意味が違うということです。

鬼滅の刃風にインターフェイスの拡張を整理する

インターフェイスの拡張は、能力の約束を親から子へ進化させていく仕組みです。

iBreathMovable は、呼吸を使って動けるという基本の約束です。
iBattleSlayer は、その約束を受け継ぎ、さらに剣士情報を表示する show() を追加します。

そして WaterHashira が iBattleSlayer を実装すると、親の move() も、子の show() も、両方実装する必要があります。

鬼滅の刃風の流れJavaの対応
呼吸で動ける基本能力iBreathMovable
呼吸で動けて、情報も示せる発展能力iBattleSlayer extends iBreathMovable
実際に能力を持つ水柱剣士WaterHashira implements iBattleSlayer
基本能力と発展能力を実装するmove()、show() を定義する

つまり、インターフェイスの拡張は、
能力の約束を段階的に積み上げる仕組み
です。

この感覚がつかめると、インターフェイスは単なるメソッド名の集まりではなく、能力や役割を階層的に整理するための強力な道具だと分かります。

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

ポイント内容
インターフェイスの拡張インターフェイス同士に親子関係を作る仕組み
スーパーインターフェイス基本となる能力の約束
サブインターフェイス親の約束を受け継ぎ、新しい約束を追加する
使うキーワードインターフェイス同士では extends
クラスの実装クラスからインターフェイスへは implements
実装クラスの責任親インターフェイス由来のメソッドも含めて定義する
クラス継承との違いクラスは型の土台、インターフェイスは能力の約束
設計上の利点基本能力と発展能力を段階的に整理できる

インターフェイスの拡張を使うと、能力の約束を一気に詰め込むのではなく、段階的に整理できます。

基本の能力はスーパーインターフェイスへ。
発展した能力はサブインターフェイスへ。
実際のクラスは、それらの約束をまとめて実装する。

この流れが分かると、インターフェイスの設計がかなり見通しよくなります。