社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

Java インターフェース メリット わからない

もう何度も実装したことがあるにも関わらず、そのメリットがいまいちピンときていなかったりします。
ネットで検索すると、メリットについて語っている色々な情報が出てきます。
その時は何となく理解できた気になるのですが、しばらくするとまたアレなんだっけ?という疑問がでてくるループに入っています。

1. インターフェースとは、処理は具体的に書かれていない、メソッドの型だけを宣言したもの

インターフェースがどういうものかは分かりますが、これだけではメリットについては何も分からないです。
処理を具体的に書かないと、何の役にも立たないのに、何でそんなことをするのか?という疑問が残ります。。

2. 処理内容を具体的に書いていないから、後で変える事ができる

なぜ、処理内容を後で変える事ができるとうれしいのでしょうか。
結局実装するのだから、それを後回しにしているだけのようにも聞こえますが、それがメリットなのでしょうか。。

3. インターフェースを実装したクラスではメソッドの実装が必須だ

そのメソッドを実装することを強制することができるのは、実装漏れを防ぐというメリットがあるのでしょうか。。
何れにしても、欲しいメソッドを実装していなければ処理は動かないのだから、実装漏れすることは無いように思います。

4. 処理は別で記述する方が、後々の変更などが予想される場合には便利

後々の変更というよりは、新しいクラスを追加し、それに実装するという意味かなと思います。
それが便利なのでしょうか。。

5. インターフェースは多重継承のように複数のインタフェースを実装可能

複数のインターフェースを実装するということは、それだけ実装しなければいけないメソッドが増えることになります。
実際に複数のインターフェースを実装するとややこしいし、これをメリットと捉えることができるのでしょうか。。

6. 利用者側へこちらが指定したインターフェースの使用を要求する

内容的には、”インターフェースを実装したクラスではメソッドの実装が必須” と同じことを言っています。
ただ、こちらの表現の方が利用者側に対して何らかの実装を要求する(強制させる)という意味合いが理解しやすいです。

7. Runnable インターフェースを例に考えてみる

Java でスレッド処理を書くときに出てくる Runnable インターフェースです。

private static class MyThread implements Runnable {
    @Override
    public void run() {
        // 何かの処理
    }
}

Runnable インターフェースを実装したクラスは、run() メソッドを実装することを利用者に強制することができます。
run() メソッドに書く処理内容は利用者側に一任されており、好きな処理を書くことができます。
利用者側は以下のように Thread のインスタンスを生成することで、簡単にスレッドを作ることができます。

Thread t1 = new Thread(new MyThread());
t1.start();

もし、MyThread とは違う処理をするスレッド(MyThread2 )を作りたくなったとします。

private static class MyThread2 implements Runnable {
    @Override
    public void run() {
        // 何かの処理
    }
}

この場合も同じく利用者側に要求されるのは、run() メソッドの実装です。
逆に言うと、それ以外には利用者側に要求されていません。
同じく run() メソッドに処理を実装することで、異なる処理を持つスレッドを作ることができます。

Thread t2 = new Thread(new MyThread2());
t2.start();

要はインターフェースが同じなので、利用者側に実装を要求されるメソッドも同じものになります。

この観点からすると、インターフェースは利用者側にとってメリットがあるように思えます。
利用者側がその機能を利用するためには、何を実装する必要があるのかが、明示されているからです。

8. インターフェースで疎結合を実現することができる

疎結合、すなわち各クラス間での依存性を低くすることで、変更に強いアプリケーションを作ることができるという意味かと思います。
以下の例が分かりやすかったです。
悪い例:

public class PC {
    public void setKeyboard(Keyboard keyboard) {};
    public void setMouse(Mouse mouse) {};
}
public class Keyboard {}
public class Mouse {}

良い例:

public class PC {
    public void setUsbDevice1(USB usb) {};
    public void setUsbDevice2(USB usb) {};
    public void setUsbDevice3(USB usb) {};
}
public class Keyboard implements USB {}
public class Mouse implements USB {}

悪い例の方は、PC クラスが Keyboard クラスと Mouse クラスに依存してしまっていて、仮に Keyboard クラスが削除された場合、PC クラスも修正しなければならないし、もし Printer クラスが追加された場合、PC クラスも修正しなければならない。
良い例の方は、USB というインターフェースを介しているので、仮に Keyboard クラスが削除された場合でも、PCクラスに修正は発生しないし、もし Printer クラスが追加された場合、USB インターフェースを実装すれば、PC クラスを修正することなく、既存のメソッド setUsbDevice[1-3] を利用することができます。

なるほど、具体的な例を見てみると、より納得感があるように思います。
今のところ、以下の2点がメリットと思えてきました。
1.利用者側に必要な実装を明示できること
2.クラス間の疎結合を実現できること

基本的に利用者側にメリットがありそうです。
あとは、実際にこれらを意識して、設計・プログラミングができるかどうか。1はまだしも、2がやや難しそうです。