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

インターフェイスの拡張は、基本の能力を受け継ぎ、さらに上位の奥義へ進化させるしくみです。

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

たとえば、ドラゴンボールの世界観で考えると、戦士にはいろいろな能力があります。

能力の約束ドラゴンボール風のイメージ
移動できる舞空術で空中を移動できる
気を解放できるreleaseKi() で気を放てる
戦闘できる必殺技や格闘で戦える
仲間を支援できる治療や補助の力を使える

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

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

ドラゴンボール風にたとえると、最初に「舞空術で移動できる」という基本能力の約束があり、そこから「舞空術で動けて、さらに気を解放できる」という上位の約束へ進化していくイメージです。

この記事では、インターフェイスの拡張を、能力の約束が親から子へ受け継がれ、さらに強い約束へ発展するしくみとして解説します。

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

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

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

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

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

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

用語意味ドラゴンボール風のイメージ
スーパーインターフェイス基本となる能力の約束舞空術で移動できる
サブインターフェイス基本の約束を受け継いだ発展版舞空術で動けて、気も解放できる

サブインターフェイスは、スーパーインターフェイスの約束を受け継ぎます。

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

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

この図が示していること

この図では、iSkyMovable から iKiFighter へ能力の約束が受け継がれる流れを表しています。

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

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

流れ内容
iSkyMovablemove() の約束を持つ
iKiFightermove() を受け継ぎ、releaseKi() を追加する
SkySaiyanmove() と releaseKi() を実装する

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

ドラゴンボール風に考えるインターフェイスの拡張

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

これは、舞空術で移動できる者という約束です。

interface iSkyMovable
{
    void move();
}

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

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

これは、舞空術で移動できるうえに、気を解放して戦える者という発展した約束です。

interface iKiFighter extends iSkyMovable
{
    void releaseKi();
}

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

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

インターフェイス持っている約束
iSkyMovablemove()
iKiFightermove()、releaseKi()

ドラゴンボール風にたとえると、iSkyMovable は「舞空術で移動できる」という基本能力です。

iKiFighter は、その能力を前提にして、さらに「気を解放して戦える」という追加能力を持つ上位ルールです。

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

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

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

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

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

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

ドラゴンボール風にたとえると、クラスの継承は「サイヤ人からスーパーサイヤ人へ進化する存在の系譜」です。

インターフェイスの拡張は、「舞空術で動ける」「気を解放できる」「仲間を支援できる」といった能力ルールの系譜です。

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

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

interface iKiFighter extends iSkyMovable
{
    void releaseKi();
}

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

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

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

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

ドラゴンボール風にたとえると、extends は「能力ルールが進化する」ことです。

implements は「実際の戦士クラスが、その能力ルールを身につけること」です。

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

ここはとても大切です。

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

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

interface iSkyMovable
{
    void move();
}

interface iKiFighter extends iSkyMovable
{
    void releaseKi();
}

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

class SkySaiyan implements iKiFighter
{
    public void move()
    {
    }

    public void releaseKi()
    {
    }
}

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

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

ドラゴンボール風にたとえると、SkySaiyan が「気を解放して戦える上位戦士です」と宣言するなら、前提となる「舞空術で動ける」能力も持っていなければならない、ということです。

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

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

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

たとえば、次のように分けると整理しやすくなります。

段階インターフェイス約束
基本能力iSkyMovable舞空術で移動できる
発展能力iKiFighter舞空術で動けて、気を解放できる
さらに発展iSupportFighter舞空術で動けて、気を解放できて、仲間を支援できる

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

ドラゴンボール風にたとえると、最初は「舞空術で動ける」、次に「気を解放できる」、さらに「仲間を支援できる」と、修行によって能力が段階的に増えていくイメージです。

ファイル名:Sample5.java

interface iSkyMovable
{
    void move();
}

interface iKiFighter extends iSkyMovable
{
    void releaseKi();
}

class SkySaiyan implements iKiFighter
{
    private String name;
    private String kiStyle;

    public SkySaiyan(String n, String ks)
    {
        name = n;
        kiStyle = ks;
        System.out.println(name + " 気の型「" + kiStyle + "」を使う空中戦士を作成しました。");
    }

    public void move()
    {
        System.out.println(name + "は舞空術で空中を素早く移動します。");
    }

    public void releaseKi()
    {
        System.out.println(name + "は気の型「" + kiStyle + "」で金色の気を解放します。");
    }
}

class Sample5
{
    public static void main(String[] args)
    {
        SkySaiyan warrior1 = new SkySaiyan("悟飯", "魔閃光");

        warrior1.move();
        warrior1.releaseKi();
    }
}

実行結果

悟飯 気の型「魔閃光」を使う空中戦士を作成しました。
悟飯は舞空術で空中を素早く移動します。
悟飯は気の型「魔閃光」で金色の気を解放します。

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

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

interface iSkyMovable
{
    void move();
}

これは、舞空術で移動できる者の約束です。

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

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

ドラゴンボール風にたとえると、これは「空中を自由に移動できる者」という基本能力の札です。

ただし、このインターフェイスだけでは、気を解放して戦う約束までは持っていません。
あくまで、移動能力に関する基本の約束です。

iKiFighter は iSkyMovable を拡張している

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

interface iKiFighter extends iSkyMovable
{
    void releaseKi();
}

ここでは、iKiFighter が iSkyMovable を extends しています。

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

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

ドラゴンボール風にたとえると、iKiFighter は「舞空術で動ける」ことを前提にして、さらに「気を解放できる」という発展した能力ルールです。

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

SkySaiyan は iKiFighter の約束を実装している

SkySaiyan クラスは、iKiFighter を implements しています。

class SkySaiyan implements iKiFighter

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

しかし、iKiFighter は iSkyMovable を拡張しています。

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

public void move()
{
    System.out.println(name + "は舞空術で空中を素早く移動します。");
}

public void releaseKi()
{
    System.out.println(name + "は気の型「" + kiStyle + "」で金色の気を解放します。");
}

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

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

ドラゴンボール風にたとえると、SkySaiyan は「気を解放できる空中戦士」という上位の約束を引き受けています。

そのため、前提となる「舞空術で移動できる」という基本能力も持っていなければなりません。

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

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

メソッド由来役割
move()iSkyMovable舞空術で移動する
releaseKi()iKiFighter気を解放する

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

public void move()
{
    System.out.println(name + "は舞空術で空中を素早く移動します。");
}

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

public void releaseKi()
{
    System.out.println(name + "は気の型「" + kiStyle + "」で金色の気を解放します。");
}

この2つを SkySaiyan が両方実装しているため、SkySaiyan オブジェクトは「空中を動ける」し「気を解放できる」存在になります。

extends と implements を混同しない

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

ここはとても大切です。

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

ドラゴンボール風にたとえると、extends は「能力ルールどうしの進化」です。
implements は「実際の戦士がその能力ルールを身につけること」です。

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

図:extends と implements の違い

この図が示していること

この図では、extends と implements の違いを表しています。

左側では、iKiFighter が iSkyMovable を extends しています。
これは、インターフェイス同士で能力の約束を受け継いでいる状態です。

右側では、SkySaiyan が iKiFighter を implements しています。
これは、実際のクラスがインターフェイスの約束を守っている状態です。

キーワード使う場所意味
extendsインターフェイス同士約束を受け継いで発展させる
implementsクラスとインターフェイスクラスが約束を実装する

この図から分かることは、同じ能力の話でも、インターフェイス同士の関係と、クラスが約束を実装する関係では使うキーワードが違うということです。

クラスの継承との違い

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

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

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

クラスの継承は、「スーパーサイヤ人はサイヤ人の一種である」という関係を表すのに向いています。

インターフェイスの拡張は、「気を解放できる戦士は、舞空術で移動できる能力も前提として持つ」という能力ルールの進化を表すのに向いています。

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

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

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

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

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

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

たとえば、ドラゴンボール風に考えるなら、次のような設計もできます。

interface iAdvancedFighter extends iSkyMovable, iSupportable
{
    void releaseKi();
}

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

インターフェイス受け継ぐ約束
iSkyMovablemove()
iSupportablesupport()
iAdvancedFightermove()、support()、releaseKi()

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

ドラゴンボール風にたとえると、舞空術、支援術、気の解放という複数の修行系統を束ねて、上位の戦士能力ルールを作るようなものです。

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

この図が示していること

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

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

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

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

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

ドラゴンボール風にインターフェイスの拡張を整理する

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

iSkyMovable は、舞空術で移動できるという基本の約束です。
iKiFighter は、その約束を受け継ぎ、さらに気を解放する releaseKi() を追加します。

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

ドラゴンボール風の流れJavaの対応
舞空術で動ける基本能力iSkyMovable
舞空術で動けて、気も解放できる発展能力iKiFighter extends iSkyMovable
実際に能力を持つ空中戦士SkySaiyan implements iKiFighter
基本能力と発展能力を実装するmove()、releaseKi() を定義する

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

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

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

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

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

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

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