【Java】Javaの多重継承にまつわるモヤモヤをスッキリ整理してみた


# はじめに
最近、[Java SE 17 Programmer I (1Z0-825-JPN) 試験](https://www.oracle.com/jp/education/certification/javase-17-programmer-1/)の勉強をしていています。
予想問題で次のような観点が問われると、「え、どうだったっけ...?」と思考停止することがありました。

- 抽象クラスって多重継承できるの？
- クラスって多重継承できるの？
- インタフェースって多重継承できるの？
- 実装クラスで多重実装できるの？

これらの問題は一見バラバラに見えますが、実は**ある1つのルール**を理解しておくことで、すべて整理できると気づきました。この記事では、自分なりに見つけたその本質を共有し、同じように悩む方の参考になればと思います。

# いきなり結論
では、上記の問題に共通する“本質”とは何か？  
私がたどり着いた考えは...

### 実装クラスから見て、同じメソッド名・引数（＝シグネチャ）を持つメソッドが複数存在しないようにすること

です！
え？どういうこと？と思いますよね。
このあと順を追って説明していきます

# クラスの多重継承は行えない

まず、Javaではクラスの多重継承（`class A extends B, C`のような構文）は**できません**。これは公式ドキュメントでも明記されています。

> Each class except Object is an extension of (that is, a subclass of) a single existing class and may implement interfaces
(Objectを除く各クラスは、単一の既存クラスの拡張（つまりサブクラス）であり、インターフェースを実装することができます。)

https://docs.oracle.com/javase/specs/jls/se24/html/jls-8.html

## なぜNG？

いきなり結論の観点から考えると、もし、2つの親クラスそれぞれで同じメソッドシグネチャ（例：`void hello()`）の実装があった場合：
- `hello()`の実装も2つになってしまい、どの`hello()`を継承すればいいか曖昧になる
→ **「実装クラスから見たときに実装が複数存在してしまう」→NG！** となります。

# インタフェースの多重継承は行える

Javaではインタフェースの多重継承は**可能**です。複数のインタフェースを`extends`で継承することができますし、実装クラスが複数のインタフェースを`implements`することも可能です。

```java
interface A { void hello(); }
interface B { void goodbye(); }

// インタフェースCがインタフェースAとBを多重継承する
interface C extends A, B {}

// 実装クラスDがインタフェースCを実装する
class D implements C {
    public void hello() {
        System.out.println("Hello");
    }

    public void goodbye() {
        System.out.println("Goodbye");
    }
}
```

## なぜOK？

これも“いきなり結論”の観点で説明できます。

- インタフェースは**メソッドの宣言だけ**を行い、実装は持たない（Java 8以前の話）
- どのインタフェースに`hello()`があろうと、`hello()`の**実装自体は実装クラスで行われる**

→ **「実装クラスから見たときに`hello()`の実装は"単一"となる」→OK！** となります。

# defaultメソッドの登場

ところが、Java 8からは、インタフェースでも`default`メソッドによって**実装を持てる**ようになりました。

```java
interface A { default void hello() { System.out.println("Hello from A"); } }
interface B { default void goodbye() { System.out.println("Goodbye from B"); } }
class C implements A, B {
  
}
```

## “いきなり結論”との要点に反してない？

そう、ここで“いきなり結論”と矛盾するように見えます。
複数のインタフェースが**実装付き（default）の同じメソッド**を持っていると、
実装クラスが**どちらの実装を使えばいいか判断できない**です。
そのため、以下のようなソースコードは、コンパイルエラーとなります。

```java
interface A { default void hello() { System.out.println("Hello from A"); } }
interface B { default void hello() { System.out.println("Hello from B"); } }
class C implements A, B {
    // コンパイルエラー: AとB、どちらのhello()を使えばいいか分からない
}
```

解決方法としては、**明示的にオーバーライド**します。
こうすることによって、同じシグネチャの実装が単一となり、"いきなり結論”にも当てはめることができました。

```java
class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // または B.super.hello()
    }
}
```

# 余談：そもそもインタフェースの多重継承も制限すべきでは？

上記の通り、defaultメソッドの登場によって、インタフェース同士でもメソッドの衝突が起きるようになりました。
そのため「インタフェースの多重継承も禁止した方が良いのでは？」と私は感じました。

この疑問をAIにぶつけてみたところ、以下のような返答をもらいました：

> defaultメソッドは後発で導入された機能であり、Javaの既存の柔軟性（インタフェースの多重継承）を壊さずに、実装の共有を可能にした設計です。
>
> 衝突の可能性がある場合は、開発者が明示的に解決することで安全性と柔軟性の両立を図っています。

なるほど、つまり、

- 既存バージョンとの互換性や、柔軟性（多重継承）は維持しつつ
- 衝突時は**開発者に責任を持たせる**設計

とすることでバランスが取られている、ということですね。

# 冒頭で挙げた問題の回答
まとめへ入る前に、冒頭で挙げた問題の答えをここで簡単に整理しておきます：
- 抽象クラスって多重継承できるの？
  - **できません。** クラスの多重継承自体が禁止されているため、抽象クラスでも同様です
- クラスって多重継承できるの？
  - **できません。** 上記と同じ理由です
- インタフェースって多重継承できるの？
  - **できます。** 複数のインタフェースをextendsで継承できます
- 実装クラスで多重実装できるの？
  - **できます。** 複数のインタフェースをimplementsできます。ただし、defaultメソッドが衝突する場合は明示的なオーバーライドが必要です


# おわりに

Javaの継承仕様を整理すると、「**実装クラスから見て、同じメソッド名・引数（＝シグネチャ）を持つメソッドが複数存在しないようにする**」という原則が一貫している、というのが私なりの理解です。

defaultメソッドを考慮すると、一見この原則に反するようにも思えますが、これはJavaの互換性を維持するための設計です。明示的にオーバーライドすることで、結果としてこの原則にも合致するようになっています。

これを意識すれば、抽象クラス・インタフェース・defaultメソッドの扱いにも納得がいきやすくなります。

資格試験のための知識も、こうした「ルールの背景」を理解しておけば、暗記に頼らず理解で乗り切れるはずです。この記事が少しでもその助けになれば幸いです！