インターフェースについて(java)
javaをはじめとするオブジェクト指向の言語に特有の
ポリモーフィズム(多態性)とインターフェースの関係について
「はじめてのjava入門」他、参考サイトを見ても、「インターフェース」ってそういうことではないのでは、、という事例をたくさんみかけたので、備忘録的に自分の考え方と、ソースをこの記事に残しておく。
(念のため付け足しておきますが、java勉強歴は1週間ほどです。。)
【参考サイト】
①「浅煎りコーヒーJavaアプリケーション入門」http://msugai.fc2web.com/java/interface.html 結論、このサイトが一番ピンときた。
②@IganinTeaさんの記事
https://qiita.com/IganinTea/items/e1d35db0a14a84bda452 この方のソースも「インターフェース」って、そういうことだよね、と納得できるものでした
内容的に、私にはまだ難しいのですが、、ソースでのインターフェースの使い方には納得できました。
③「はじめてのjava入門」http://www1.bbiq.jp/takeharu/java104.html
※なんとなく、あまりあてにならないかな、、と思い始めたサイト
「はじめて」と「入門」も同意反復だし、、あえて?
概念的に「インターフェース」ではなく、継承だよね、という例でした。
④「侍」https://www.sejuku.net/blog/21542
③同様、こんな初心者が言うのもあれですが、インターフェースの使い方が「??」の例です。「それは継承の例では?」。
インターフェースとは?
参考サイト①によると
「インタフェースは、ロジックを持たずに、パッケージpublicなメソッドのシグネチャのリストです。」とのこと。
シグネチャとは、「署名」を意味しますから、インターフェースとは
「同一パッケージ内で使用できるメソッドの名称一覧」と言っています。
なんとなく、僕はこの説明が一番腑に落ちました。
インターフェースにおいて、メソッドは名称のみ定義されています。
メソッドのロジックは、インターフェースを実装(implements)するクラスにおいて、オーバーライドする必要があります。オーバーライドしないと、コンパイルエラーとなります。「実装の強制」ができる、ということです。
つまり、このインターフェース(ふるまい)を実装したんだから、
「ちゃんとふるまえるようにしろ」ということですね。
ここまできて、「継承」との違いがなんとなくわかってきたのですが、「継承」は、「動物」→「猫」のような上位下位の関係を概念的に、表す意味があります。あと、「コードを書く」と作業に対しては「記述の重複を防ぐ」というのが一義的な意味です。
継承してオーバーライドすれば、確かにインターフェースと同じように、継承先で「ふるまい」を変えることもできるのですが、、これはインターフェースでやるべきです。また、継承は一つのクラスからしかできませんが、インターフェースは、複数適用することができます。
継承を使用するときは、サブクラスが、スーパークラスと大体同じようにふるまうが、少しだけ変えたい、というように、あくまでも「記述の重複を防ぐ」という一義的な目的に沿うようなときに使うべきなのでしょう。
なお、インターフェースに近い概念として、抽象クラスがあります。参考サイト②にあるような、実装する際の勘所というのは、まだ私にはつかめないところもありますが、そもそもの発想が異なる、と覚えておくこととします。
インターフェースは「ふるまい」の一覧であり、上位下位の関係は意識していません。(多分)なので、この上位下位の関係性については、フォルダ構成などでうまく表現していく必要があるのでしょう。(また勉強してみます。)
最近の新しい言語(Go言語など)では、抽象クラスがなくなっているそうですね。インターフェースは比較的新しい考え方であり、継承→抽象→インターフェース(ふるまいを与える)とオブジェクト指向言語も進化をしているのだな、くらいの理解にとどめておいたほうがいいのかもしれません。。プログラミング初心者にとっては、この「歴史」が学習のネックとなってきますが、、掘れば「こういう歴史があってな」という記事はいくらでもでてきますので、疑問におもったらすぐ調べ、一つ一つ疑問を解消していけばいいのでしょう。
インターフェースは、クラスに「ふるまい」を与えることができる。
インターフェースは「runnable」に代表されるように、よく「~できる」という名称がつけられています。直訳すると「走ることができる」、ですね。つまり、この「runnable」というインターフェースを実装したクラスは、「走る」というふるまいができるようになる、ということです。
例として、僕が作成したサンプルソースを下部に添付しておきます。このサンプルソースでは、「barkable」→「吠えられる」という「ふるまい」をインターフェースとして実装してみました。
barkableというインターフェースではbark(吠える)というメソッドを定義しています。先に説明したとおり、bark内にはロジックは記述しておりません。
その下で、LionとCatというクラスを用意しています。この2つのクラスに、barkableというインターフェースを実装することで、このクラスに「吠える」というふるまいをさせることができます。
なお、インターフェースを使用する際に、大事なのが、Lion、Catを呼び出すクラスでは(サンプルソースでいうとMyAppというクラス)、Runnableインターフェースが実装されているということさえ、わかっていれば、Lion、Cat内でのbarkメソッドの実装を意識することなく、Lion、Catにbark(吠え)させることできるのです。(実装を隠蔽することを、カプセル化といいます)
パッケージ内で利用可能なのですから、他のクラスで、他の動物に吠えさせたいときは、barkableを実装し、barkメソッドを使ってやれば、好きな吠え方をさせられるし、barkableを実装しているクラスだとわかっていれば、いくらでも吠えさせることができるのです(笑)
【サンプルソース】
interface barkable{
void bark();
}
class Rion implements barkable{
@Override
public void bark(){
System.out.println("ガオー!!");
}
}
class Cat implements barkable{
@Override
public void bark(){
System.out.println("ニャオー!!");
}
}
public class MyApp{
public static void main(String[] args){
//このように実装を意識せず「吠えさせる」というふるまいを記述
//確かに可読性も高いです
Rion ri = new Rion();
ri.bark();
Cat ca = new Cat();
ca.bark();
}
}
【実行結果】
[vagrant@localhost java_lessons]$ javac MyApp.java
[vagrant@localhost java_lessons]$ java MyApp
ガオー!!
ニャオー!!
参考サイトについて
参考サイト③では、Animalをインターフェースとして定義しています。Animalというのは「モノ」を表します。「動物としてのふるまい」を定義している、とも解釈できますが、インタフェースの概念にはなじまないのかな、、と思いました。(そこから、動物でインターフェースを表現するなら、、とサンプルソースを書いてみた次第です。)
参考サイト④では、calc(計算)というインターフェースを作成しています。
それをAdd(足し算)、というクラスに適用しています。
どちらかというと、「計算」というクラスに、「足し算」というふるまいを適用させたほうが、「インターフェース」という概念にフィットするサンプルになりそうです。
あと「ふるまいの名称」であるメソッドの名称がmethod1、method2では、インターフェースの意味がない気がします。。「ふるまいの名称の一覧」なので、ふるまいとして、わかりやすい「名称」としあげることは、インターフェースの説明として、必要不可欠です。
ものすごい混乱するから、やめて!
この記事が気に入ったらサポートをしてみませんか?