Javaのimplementsはなぜ必要?interfaceを使う目的と継承との違いをわかりやすく解説

Java

Javaを学んでいると、classの継承だけでなくimplementsによるinterface実装が登場します。しかし、最初のうちは「結局メソッドを書くのはクラス側なのだから、interfaceを分ける意味が分からない」と感じる人も少なくありません。

特に、class CS implements CA, CB のような形を見ると、「わざわざCAやCBに空のメソッド定義を書くより、CSの中に全部書けばいいのでは?」と思いやすいです。

この記事では、implementsの本当の目的、継承との違い、interfaceを使うメリットについて、具体例を交えながら整理して解説します。

implementsは「アクセス制限」のためだけではない

質問で挙げられているように、interfaceを使うと「CAだけ使う人」「CBだけ使う人」に役割を分けることができます。

たしかにこれはinterfaceの重要な効果の一つです。

例えば。

interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

class MultiMachine implements Printer, Scanner {
    public void print() {
        System.out.println("印刷");
    }

    public void scan() {
        System.out.println("スキャン");
    }
}

Printer型で受け取ればprint()しか見えません。

Printer p = new MultiMachine();
p.print();

これは「不要な操作を見せない」という設計になります。

ただし、implementsの目的はそれだけではありません。

interfaceの本質は「機能の約束」を定義すること

interfaceの最も重要な役割は、「この機能を持っています」という契約を定義することです。

つまり、実装の中身ではなく、「何ができるか」を統一するために使います。

例えばJava標準ライブラリにはRunnableというinterfaceがあります。

interface Runnable {
    void run();
}

ThreadはRunnable型を受け取ります。

Thread t = new Thread(runnableObject);

Thread側は「run()が呼べればよい」ので、そのクラスが何者かを知る必要がありません。

つまりinterfaceは、「具体的なクラスではなく、能力で扱う」ための仕組みなのです。

継承とimplementsは目的が違う

質問にもあるように、継承ではoverrideによるポリモーフィズムが重要です。

例えば。

class Animal {
    void speak() {}
}

class Dog extends Animal {
    void speak() {
        System.out.println("ワン");
    }
}

Animal型で扱ってもDogのspeak()が呼ばれます。

Animal a = new Dog();
a.speak();

これは「共通の親を使って複数の子を統一的に扱う」ための仕組みです。

一方、implementsは「共通の能力を保証する」ためのものです。

つまり。

機能 目的
extends 共通の性質を継承する
implements 共通の機能契約を保証する

という違いがあります。

implementsの真価は「差し替え可能性」にある

interface最大の強みは、実装クラスを自由に交換できることです。

例えば。

interface Payment {
    void pay(int price);
}

これを実装するクラスが複数あるとします。

class CreditCardPayment implements Payment {}
class PayPayPayment implements Payment {}
class CashPayment implements Payment {}

利用側はPayment型だけを見れば済みます。

void shopping(Payment p) {
    p.pay(1000);
}

こうすると、後から新しい決済方法を追加しても、shopping()側は変更不要です。

これがinterface設計の非常に大きな価値です。

「CSに全部書けばいい」が危険になる理由

小規模なプログラムでは、確かにCSに全部書いても問題ないように見えます。

しかし大規模開発では、「どの機能を保証しているか」を分離しないと管理不能になります。

例えば。

  • ネットワーク送信できる
  • 保存できる
  • ログ出力できる
  • シリアライズ可能

などを全部classだけで管理すると、依存関係が複雑になります。

interfaceを使うと、「この機能だけを必要とするコード」を作れるため、変更に強くなります。

これはJavaの設計思想である「疎結合」に直結します。

実はJava標準ライブラリもinterfaceだらけ

Java標準ライブラリではinterfaceが大量に使われています。

代表例。

  • List
  • Map
  • Set
  • Comparable
  • Runnable
  • Serializable

例えば。

List<String> list = new ArrayList<>();

これはArrayListという具体クラスではなく、「Listとして使う」という設計です。

後からLinkedListに変えても、利用側コードをほぼ変更せずに済みます。

つまりinterfaceは、実装を隠しながら機能だけを公開するための道具なのです。

まとめ

implementsは単なるアクセス制限のためだけではありません。

その本質は。

  • 「何ができるか」を定義する
  • クラス同士を疎結合にする
  • 実装を差し替え可能にする
  • 大規模開発で変更に強くする
  • 共通の機能契約を保証する

という点にあります。

最初は「interfaceは中身が空なのに冗長では?」と感じやすいですが、実際には「実装」と「機能の定義」を分離することが、Java設計の重要な思想になっています。

特に複数人開発や長期保守では、この分離がコード品質を大きく左右します。

コメント

タイトルとURLをコピーしました