初心者がインターフェースの意義を感じた話
Javaを学んでいる中で、インターフェースという概念が出てきました。しかし、インターネットで調べたり、AIに聞いたりしても実装方法や注意点の解説が主で、実際の「存在意義」を理解するには苦労しました。Java初心者として、クラスと絡めて考えるとインターフェースの意義がより明確に理解できたので、ここでそのポイントを共有します。
クラスとインターフェースの役割
まず、クラスは「オブジェクトの属性(変数)と動作(メソッド)をまとめて管理」するために使われ、特定の概念や物を表現します。例えば、Baseball、Bowling、Javelin Throwといったスポーツにおいて、それぞれが固有の属性や動作を持っています。これを次のように表にまとめられます。
name | 属性 | メソッド |
---|---|---|
Baseball | pitcher | throw |
Bowling | player | throw |
Javelin Throw | thrower | throw |
インターフェースは、異なるクラスに共通の「振る舞い」(メソッド)を提供するために存在します。例えば、throwという動作が複数のクラスで必要ならば、Throwableインターフェースとして定義して各クラスに適用することができます。これにより、「投げる」という動作は一貫して提供され、クラスごとにその具体的な内容(例えば、投球の速度やボールの重さなど)をカスタマイズできます。
このように、クラスは横軸、インターフェースは縦軸を管理する役割を持つと考えられます。また、クラスは複数のインターフェースを実装できるため、クラスごとに異なる振る舞いを付加できます。
name | 属性 | メソッド1 | メソッド2 |
---|---|---|---|
Baseball | pitcher | throw | isSuccess |
Bowling | player | throw | isSuccess |
Javelin Throw | thrower | throw | isSuccess |
さらに、各クラスごとに固有のメソッドを追加することも可能です。
name | 属性 | メソッド1 | メソッド2 | メソッド3 |
---|---|---|---|---|
Baseball | pitcher | throw | isSuccess | getPitchCount |
Bowling | player | throw | isSuccess | isSecondThrow |
Javelin Throw | thrower | throw | isSuccess | checkWindConditions |
抽象クラスとの違い
このように考えると、抽象クラスとの使い分けも理解しやすいと思います。抽象クラスもインターフェースも概要だけ決めて詳細は各クラスで定義しますが、インターフェースはクラスを横断して振る舞いを提供するのに対し、抽象クラスは属性や基本的なメソッドの共有を目的としています。
インターフェースのメリット
インターフェースをclass クラス名 implements インターフェース名{}
で実装することで、必須メソッドの未実装を防ぎ、異なるクラス間でも共通のメソッドが存在するため、コードの理解がしやすくなります。Javaプログラムを本格的に作成した経験はまだありませんが、共通メソッドの「定義書」としてインターフェースが役立つという理解ができたことは大きな収穫でした。
Discussion
ふと、自分の記事からリンクが見れたのでコメント致しました。
Go言語のインターフェースも学ぶと面白いですよ。
Javaは
「振る舞い(メソッド)も宣言(implements)もクラスに必要」
ですが、Go言語は
「振る舞い(メソッド)が全て揃っていれば宣言(implements)が不要」
となります。
よくある比喩で言うと、
というみなし方です。
なので、Javaは変数の扱い方も面倒になります。
以下のサンプルで
I
とI2
は同一のメソッドシグネチャを持つのに、変数で
C
を参照できるのはI
だけです。また、インターフェースの実用としては「依存性の逆転」という難しい言葉が使われるケースもあり、
要は「クラス同士の依存関係をインターフェース一つに集中させよう」とワンクッション置く設計の考え方があります。
これがあると、「インターフェースの変更が無い限りは実装クラスの修正が楽になる」という特徴があります。
特に独自コードというよりは、外部のAPI等を扱う時に差し替えやすくするような使い方をします。
(リリース後の保守プロセス等を想像すると、よりメリットが分かり易くなります。「安定稼働しているサービスに影響範囲の広い変更を入れ込む恐怖」とかです)
大規模開発であれば、担当者と機能も大量になるので、お互いの変更が 直接 影響しあわないようにインターフェースを設計するのが慣習です。
また、インターフェースは
default
メソッドも扱えるようになり、共通的に実装したい処理をわざわざインターフェースの外(個々の実装クラスや、共通のユーティリティクラス)に書く必要が無くなりました。
インターフェースの事を良く理解しようとされているようでしたので、老婆心ながら長文コメント致しました。失礼致します。
コメントありがとうございます🙏
言語によってインターフェースの設計思想が違うのは面白いですね。自分はまだjavaの勉強に取り組み始めたばかりですが、そのようなお話を伺うことができ、 Go言語にも興味が湧いてきました。
自分は分析畑のキャリアだったので、このあたりの実務ベースの利点や慣習のお話は大変腑に落ちました。もっと精進せねばですね。