さて、使い方について理解したところで、次はインターフェイスを自作する方法について見てみましょう。
最初に「インターフェイスとは、具体的な処理が書かれていないメソッドの型だけを宣言している特別なクラスのようなものです」と書きました。このことから分かるように、インターフェイスは基本的にメソッドの型だけを宣言します。文法的には次のように表記します。
interface インターフェイス名 {
メソッドの宣言
}
フィールド宣言もできますが、本連載ではこの基本を押さえることに専念しましょう。
メソッドの宣言については、次のようになります。クラスでのメソッド宣言では処理を記述するために「{ 処理 }」という部分がありましたが、インターフェイスのメソッド宣言ではこの部分はありません。これは、インターフェイスでは実際の処理は必要ないからです。
戻り型 メソッド名(仮パラメータ型 仮パラメータ名);
void メソッド名(仮パラメータ型 仮パラメータ名);
ここでは、仮パラメータを1つ持つメソッドで戻り型を持つものとvoid型のものだけを紹介しましたが、仮パラメータがないメソッドや複数の仮パラメータを持つメソッドなども宣言できます。その場合は「{ 処理 }」がないというのは同じです。もちろん複数のメソッドを宣言することもできます。
ここで、「第8回 Javaのメソッドを理解する」で作成したDiceクラスを思い出してみましょう。最初に、Diceクラスは「int roll()」というメソッドを持つように設計をしました。その後で、「イカサマサイコロ」と「通常のサイコロ」の2つの実装方法を紹介しました。第8回の時点ではDiceクラスのプログラムコードを直接書き換えましたが、インターフェイスを使えば、両方とも用意して切り替えを簡単にできるようになります。今回はインターフェイスを使って、このDiceを実現してみましょう。
Eclipseの操作手順は以下のとおりです。まずは、DiceIFインターフェイスを作成してみましょう。
出来上がったインターフェイスDiceIFはメソッド「int roll()」を持つので、このメソッドを宣言します。そこで、次の水色部分を追加します。
解説のときには付けませんでしたが、interfaceとメソッド宣言の前にpublicという修飾子が付いています。これらについては、ここではおまじないだと思って付けてください。
次に、このDiceIFを実装するクラスとして「いかさまダイスのFakeDice」と「本物ダイスのRealDice」を作成します。まず、次のようにFakeDiceクラスを作成します。
同様にしてRealDiceクラスを作成します。生成されたDiceFakeクラスとRealDiceクラスへフィールドを追加し、public int roll() { } 内の処理を変更します。具体的には、次の水色部分のように修正します。コードの内容については第8回のものと同じなので、そちらを参照してください。
最後にこれらを使用するサンプルクラスSample101を作成します。
java.util.Listを使ったときと同様にして、DiceIFインターフェイスをパラメータとして持つメソッドrollsを用意して実行します。rollsメソッドでは単純に6回ダイスを振って、その結果を出力するという処理となっています。FakeDiceを使いたいときには、rollsへFakeDiceオブジェクトを渡しますし、RealDiceを使いたいときには、rollsへRealDiceオブジェクトを渡しています。
実行結果は画面6のようになります。FakeDiceの結果は何度実行しても変わりませんが、RealDiceの結果は実行するたびに変わっているはずです。
FakeDice
4
2
1
5
6
3
RealDice
1
5
5
4
1
3
いかがでしょうか、java.util.Listを使ったプログラムと同じようなプログラムを自作することができました。このようにインターフェイスを使えば、Diceクラスのコードをいちいち書き換えなくても、FakeDiceとRealDiceを簡単に切り替えることができることも理解できたと思います。2つのインターフェイスのサンプルプログラムを通して、インターフェイスの便利さを理解してもらえたでしょうか。
コラム extends
FakeDiceクラスもRealDiceクラスも、DiceIFインターフェイスをimplementsすることで、「int roll() というメソッドを持ち、メソッドの処理コードを記述している」ことが分かるようになっていました。このとき、「int roll() というメソッドを持つ」という点に着目した場合は、これらはインターフェイス継承の関係にあるといいます。つまり「FakeDiceもRealDiceもDiceIFのインターフェイス継承をしている」といいます。Javaのimplementsでは、インターフェイス継承と、インターフェイスの実装(メソッドの処理コード記述)をしているという両方の意味を含んでいます。
似たような機能として、実装継承というものもあります。こちらは、インターフェイスの継承だけでなくインターフェイスを構成するメソッドの処理コード自体も継承します。これは、既存のクラスを機能拡張したい場合によく使います。例えば、java.util.ArrayListクラスを機能拡張してgetメソッドの処理が何回呼ばれたかというメソッドを1つだけ追加したいというようなときに便利です。こんなときにJavaでは、java.util.ArrayListと同じようなクラスを自作する必要はなく、キーワードextendsを使って、java.util.ArrayListが実装しているコードをそのまま継承して、差分だけコーディングするという方法が取れます。ちなみに、Javaのextendsは、インターフェイス継承と実装継承のどちらも可能であるため、単に継承と訳されます。
implements、extends、継承、インターフェイス継承、実装継承といった用語の詳細やそれぞれの関係について興味を持った読者は、これらについて調べてみてください。
今回はインターフェイスについて解説をしました。インターフェイスは重要な概念ですが奥が深いので、すぐにすべてを理解することは難しいと思います。まずは、JavaのAPIで用意されているクラスについて、インターフェイスを実装している場合は、ほかの似たようなクラスと簡単に切り替えができるようにインターフェイスを使えるようになってください。インターフェイスを使ったプログラミングの経験を積むと、自然と自作インターフェイスの設計ができるようになるはずです。インターフェイスを有効に活用することができるようになると、変更に強いプログラミングができるようになります。ぜひ積極的に使うようにしましょう。
インターフェイスを使ったプログラミングができるようになると、同じ名前のクラスを作りたくなることが多くなります。例えば、本当はDiceという名前のインターフェイスを今回作りたかったのですが、DiceIFという名前で作成しました。第8回のときにすでにDiceクラスというものを作成してしまっていたため、同じ名前となるDiceインターフェイスを作成することができなかったからです。こういった問題を解決するために、Javaにはパッケージという概念が導入されています。次回はこのパッケージについて解説をする予定です。
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。Ja-Jakartaプロジェクト(http://www.jajakarta.org/)へ参加し、PMCの一員として活動を支えている。また、長野県の地域コミュニティである、SSS(G)(http://www.sssg.org/)やbugs(J)(http://www.bugs.jp/)の活動へも参加している。
Copyright © ITmedia, Inc. All Rights Reserved.