2019/02/06
インターフェースって何なのかイマイチよく分からないという人に向けて、その概念や使い方、使いどころについて説明します。
sponsored link
インターフェースと継承
インターフェースを理解するには先に継承や抽象クラス(abstract)について理解しておいた方がいいです。もしそこらがまだよく分かっていていない人は先に継承って何?辺りから読み直すことをおすすめします。
今までクラスを書いてきましたが、クラスというのは何らかのパラメータや機能を持つオブジェクトを作るために書くのでした。具体的に言うと「食事をできる人間」だとか、「走れる車」だとかっていう風に、あくまで人間であったり車であったり、何らかのオブジェクトを作るために書くのがクラスです。
あるクラス(オブジェクト)を元に、より限定的(専門的)な機能を備えたオブジェクトを作ることができるのが継承です。具体的には「プログラミングができる人間(プログラマー)」だとか、「速く走れる車」とかっていう風に。
それに対し、インターフェースというのは、言わば何らかの機能だけを抽出したような存在です。言葉にするなら「プログラミングができる」とか、「速く走れる」みたいに、オブジェクトから分離した状態で、その機能だけを取り出して定義するのがインターフェースだと思っていたらいいです。
インターフェースは、オブジェクトと分離した存在なので、いろんなオブジェクトにその機能だけをくっつけることができます。インターフェースが持つ機能をあるクラスにくっつけることを「実装する」なんていう風に表現します。
継承の場合は、extendsというのを使いましたね。
class Child extends Super{
}
それに対して、インターフェースを実装する時は、implementsを使います。
class Human implements runnable{
}
↑Humanクラスにrunnableインターフェースを実装しています。走れる人間オブジェクトです。「runnable」というのは「run(走る)」に可能の「able」がくっついた英単語です。インターフェース名はしばしばこのように機能を表す動詞+ableという名称が使われます。
継承との大きな違いとして、インターフェースはいくつでも実装することができます。複数のインターフェースを実装するにはコンマ区切りで列挙するだけです。
class Human implements runnable, swimmable, flyable{
}
↑これで、走れて泳げて空を飛べる人間オブジェクトです。
継承とインターフェースを両方使うこともできます。
class SuperCar extends car implements flyable{ }
↑空を飛べる車(スーパーカー)オブジェクトです。
インターフェースの定義の仕方
インターフェースの定義の仕方は普通のクラスを定義するのとよく似ています。以下はFlyableインターフェースを定義しています。
interface Flyable{ void fly(); }
Flyableインターフェースのメンバはflyメソッドのみです。しかもflyメソッドの中身は定義されていません。この形、見覚えありますよね?abstractの説明で出てきた形です。インターフェースもabstractなメソッドと同じで、具体的な処理の中身はその場では定義せず、実装先のクラスで定義します。
要するに「Flyableインターフェースを実装したらflyメソッドが呼ばれるだろうから、飛べるように各自用意しておきなさいよ。」という約束事を徹底させるのがインターフェースの役割と言えます。
HumanクラスにFlyableインターフェースを実装してみます。
interface Flyable{ void fly(); } class Human implements Flyable { String name; Human(String name){ this.name = name; } @Override public void fly(){ System.out.println(this.name + "は空高く飛んだ!"); } }
実際に、Humanオブジェクトを飛ばしてみましょう。
Main.java
interface Flyable{ void fly(); } class Human implements Flyable{ String name; Human(String name){ this.name = name; } public void fly(){ System.out.println(this.name + "は空高く飛んだ!"); } } public class Main { public static void main(String[] args){ Human human = new Human("Nobuo"); human.fly(); } }
実行結果↓
飛びました。
先程はインターフェースとは機能を抽出したものと言いましたが、実際は具体的な機能については実装する際にそれぞれのクラスにて定義する必要があるわけです。枠だけを提供するような感じですね。
Flayableインターフェースを実装したクラスはflyメソッドを定義しなければコンパイルが通りません(注*)。言い方を変えると、Flyableインターフェースを実装しているクラスは、flyメソッドが呼ばれたら飛ぶことが保証されているクラスだということです。
注*)抽象クラスにして継承先に定義を先送りすることはできます。
まあNobuoが飛んだとかそんなこと言われても、インターフェースの使い所がイマイチよく分からないと思うので、続いてもう少し実践的な例で説明します。
ポリモーフィズムとは?
インターフェースの使い所を理解するにはポリモーフィズム(多態性)という概念とセットで考える必要があるかと思います。
ポリモーフィズムの定義なんて言葉で説明されてもチンプンカンプンだと思うので、それがなんとなく分かるような例を出して説明します。
アプリとそれが動くプラットフォーム(OS)の設計を想像しましょう。画面に配置できるパーツをビューと呼び、Viewクラス(抽象クラス)で定義されているものとします。Viewインスタンスはそのビューのサイズをパラメータとして持つものとします。
abstract class View{ int width; int height; View(int width, int height){ this.width = width; this.height = height; } }
このViewクラスを継承した以下のようなクラスで、実際に画面に配置されるビューを定義します。
- TextViewクラス(ただのテキスト)
- ImageViewクラス(画像を表示する為のビュー)
- ButtonViewクラス(ボタン)
TextViewクラスはこんな感じです。↓
class TextView extends View{
String text;
int textSize;
TextView(int width, int height, String text){
super(width, height);
this.text = text;
this.textSize = 10
}
}
TextViewクラスは表示する文字(テキスト)に関する情報をパラメータとして持ちます。
ImageViewクラスはこんな感じです。↓
class ImageView extends View{ String src; ImageView(int width, int height, String src) { super(width, height); this.src = src; } }
ImageViewクラスは表示する画像リソースのuriをパラメータとして持ちます。
ところで、ビューにはクリックできるものと、クリックしても何の反応もないものがありますね。クリックできるものはClickableインターフェースでその機能を付随させましょう。
interface Clickable{ public void onClick(); }
Clickableインターフェースを実装したクラス(ビュー)は必ずそのビューがクリックされた際の処理(onClickメソッド)を定義する必要があります。
クリックして反応があるものと言えば、ボタンですね。ButtonViewクラスは文字列情報も持っているべきなのでTextViewクラスを継承しつつClickableインターフェースを実装することで、ボタン機能を表現しましょう。
class ButtonView extends TextView implements Clickable{ ButtonView(int width, int height, String text){ super(width, height, text); } @Override public void onClick(){ //クリックされた際の処理 System.out.println(this.text + "がクリックされました。"); } }
と、こんな風なクラス設計にしておけば、クリックされた際の挙動に関してはそれぞれのアプリ側でonClickメソッドによって定義することができ、システム側では「ビューのクリック検知→そのビューインスタンスのonClickメソッドを呼ぶ」という非常にシンプルかつ統一的な処理でクリック時の反応を制御することができます。
もしテキストボタンではなく画像ボタンを作ろうと思えば、ImageViewを継承したImageButtonViewクラスを作ってClickableインターフェースを実装させればOKです。
class ImageButtonView extends ImageView implements Clickable{ @Override public void onClick(){ //クリックされた際の処理 } }
Clickableインターフェースを実装しているならonClickメソッドが定義されていることが保証されているので、システム側はどんなビューであれClickableインターフェースを実装しているビューならば、安心してonClickメソッドを呼ぶことが出来るわけです。まさにclickableなビューです。
このように、呼ぶ側からすれば共通のメソッド(ここで言うonClickメソッド)で定義しながらも、それぞれのクラスごとに独自の動作(処理)を定義するプログラミング手法をポリモーフィズムとか多態性と呼んでいます。
抽象クラス(abstract)やインターフェースという仕組みは、多態性(ポリモーフィズム)を上手く実現するための手段だと言えます。
インターフェースと抽象クラス
インターフェースと抽象クラスは機能的によく似ている部分が多いです。一方で決定的に違う部分もあります。
その微妙な違いをまとめておきます。
アクセス修飾子
インターフェースに付けられるアクセス修飾子はpublicだけです。そりゃそうですよね。インターフェースっていうのは他のクラスに実装することを前提に書いているんだからprivateにするって意味がわかりません。publicをつけなかった場合(アクセス修飾子なし)、同パッケージ内からの利用だけに制限されることになります。
抽象クラスはpublicだけでなくprotectedも付けることができます。protectedはサブクラスからしかアクセスできない状態です。
定義できるもの
インターフェースの中では変数(いわゆるパラメータ)は定義できません。できるのはpublic static finalなクラス定数のみです。何も修飾子を付けずに変数を宣言しても自動的(内部的)にpublic static finalな変数(クラス定数)として扱われます。
メソッドに関しては、インターフェース内で定義できるのは中身のないメソッド、つまり抽象メソッドだけ。だったのですがJava8からdefault修飾子を付けることで中身も定義できるようになりました。
メソッドに付けられるアクセス修飾子はpublicのみです。ただしメソッドにpublicを付けても付けなくても結局インターフェース自体の修飾子が優先されるので、付ける意味がありません。
抽象クラスのメンバは、特に制限なく普通のクラスと同じように変数もメソッドも定義することができます。というよりそもそも抽象クラスとは一つでもabstractなメソッドがあれば抽象クラスになるってだけの話です。
多重継承
抽象クラスの継承(抽象クラスでなくても)の場合は、一つのクラスに対して親クラスとなれるのは一つだけです。そりゃそうです。継承の本質はChild is Superです。もしDogクラスとCatクラスを両方継承してしまったら、それは犬なのか猫なのか分からなくなってしまいます。ハーフとか無しです。
一方でインターフェースは機能を付随する為のものなので、一つのクラスに対していくつでも実装することができます。犬のように吠えられるBowWowインターフェースと、猫なで声で無くことができるNyanNyanインターフェースを両方実装したHumanクラスはありです。犬のように吠えられて猫なで声でにゃんにゃん言える人間です。あくまでそいつは人間です。
インターフェースと抽象クラスの違いまとめ
機能的な違いをまとめると以下のようになります。
インターフェース | 抽象クラス | |
---|---|---|
アクセス修飾子 | public なし |
public protected なし |
定義できるメンバ | 抽象メソッド defaultメソッド クラス定数 |
制限なし |
多重継承(実装) | あり | なし(親クラスは一つ) |
ただ、機能的な違いにスポットを当ててインターフェースと抽象クラスの違いを議論してもあまり意味はないのかなと思います。最も本質的な違いは、機能ではなく思想的な部分ではないでしょうか。
抽象クラスはあくまでオブジェクトの雛形であるのに対し、インターフェースはオブジェクトに付随させる機能であると認識するのが自然です。
オブジェクトに依存した処理(機能)に多態性を持たせたければ、そのクラスのメンバとしてabstractメソッドを定義しておけばいいし、オブジェクトに依存しているとは言えない共通の処理(機能)ならばインターフェースとして抽象化しておけば、利用しやすいと思います。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
初回からこの最新の回まで一気に読ませて頂きました。
Javaの復習が必要となったため調べていてこのサイトを見つけましたが、
とても分かりやすく書いてあって、見つけられてよかった!と思いました。
こんなに分かりやすく書いてあるサイトは初めて見ました。
この回を最後にしばらく更新されておられないようなので、続きはないのかなと少し残念です。。
最後に、本当にありがとうございました!
by 匿名 2019/05/31 16:50
とても分かりやすかったです。Javaを始めるにあたっていいスタートダッシュをすることができました。
今回で最後なのであればとても残念です。お世話になりました。本当にありがとうございました。
by たん 2019/06/16 16:56
>インターフェースって何なのかイマイチよく分からないという人に向けて、その概念や使い方、使いどころについて説明します。
もう、ここまでくるとはっきりと初心者じゃなく、迷える子羊を救うために説明するって書いてある(汗
by 匿名 2019/08/28 23:06
コンストラクタ の使い方がよくわからなくて、検索しているうちに行き着きました。
目的の記事以外にも沢山の記事があり、どれもとてもわかりやすく説明されていてとても勉強になりました。
ありがとうございました!
by 匿名 2019/12/14 12:36
大きなお世話かもしれませんが、アクセス修飾子の説明、間違ってますよ。
by 匿名 2020/05/12 16:57
大変分かりやすかったです。
ありがとうございました。
by 匿名 2020/09/05 19:06
私はC#の勉強をしていてオブジェクト指向でつまづいていたのですが
このサイトにはまさにそこが知りたかったという初歩的な疑問について丁寧に書かれていたので
言語は違いますが他のどのC#の入門サイトよりも参考になりました。
ありがとうございました。
by 匿名 2021/11/11 22:10