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設計の重要な思想になっています。
特に複数人開発や長期保守では、この分離がコード品質を大きく左右します。


コメント