次に、このDiceIFを実装するクラスとして「いかさまダイスのFakeDice」と「本物ダイスのRealDice」を作成します。まず、次のようにFakeDiceクラスを作成します。
同様にしてRealDiceクラスを作成します。生成されたFakeDiceクラスとRealDiceクラスへフィールドを追加し、「public int roll() { }」内の処理を変更します。具体的には、次のように修正します。
public class FakeDice implements DiceIF { int[] values = new int[] { 3, 4, 2, 1, 5, 6 }; int current = 0; @Override public int roll() { current++; if (current == 6) current = 0; return values[current]; } }
public class RealDice implements DiceIF { java.util.Random r = new java.util.Random(); int maxNum = 6; @Override public int roll() { return r.nextInt(maxNum) + 1; } }
生成されたコードに「@Override」という行がありますが、これはアノテーションです。アノテーションの詳細は、後ほど別途解説をする予定ですが、簡単に説明をしておきます。
このアノテーションは、「インターフェイスで宣言されているメソッドを実装している」という注釈をプログラムに付けています。ここでは、DiceIFインターフェイスのroll()メソッドを実装しているという印を付けているのです。この注釈はプログラムの実行には影響がありません。
なお、ほかのコードの内容についての解説は連載第7回のものと同じなので、そちらを参照してください。
最後に、これらを使用するサンプルクラスSample92を作成します。
java.util.Listを使ったときと同様にして、DiceIFインターフェイスをパラメータとして持つメソッドrollsを用意して実行します。rollsメソッドでは単純に6回ダイスを振って、その結果を出力するという処理となっています。FakeDiceを使いたいときには、rollsへFakeDiceオブジェクトを渡しますし、RealDiceを使いたいときには、rollsへRealDiceオブジェクトを渡しています。
public class Sample92 { public static void main(String[] args) { Sample92 app = new Sample92(); System.out.println("FakeDice"); app.rolls(new FakeDice()); System.out.println("RealDice"); app.rolls(new RealDice()); } public void rolls(DiceIF dice) { for (int i=0 ; i<6 ; i++) { System.out.println(dice.roll()); } } }
実行結果は下記のようになります。FakeDiceの結果は何度実行しても変わりませんが、RealDiceの結果は実行するたびに変わっているはずです。
Sample92.javaの実行結果
FakeDice 4 2 1 5 6 3 RealDice 6 5 2 3 1
いかがでしょうか。java.util.Listを使ったプログラムと同じようなプログラムを自作できました。このようにインターフェイスを使えば、Diceクラスのコードをいちいち書き換えなくても、FakeDiceとRealDiceを簡単に切り替えができることも理解できたと思います。
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は、インターフェイス継承と実装継承のどちらも可能であるため、単に「継承」と訳されます。
今回は、インターフェイスについて解説をしました。2つのインターフェイスのサンプルプログラムを通して、インターフェイスの便利さを理解してもらえたでしょうか。インターフェイスは重要な概念ですが奥が深いので、すぐにすべてを理解することは難しいと思います。
まずは、JavaのAPIで用意されているクラスについて、インターフェイスを実装している場合は、ほかの似たようなクラスと簡単に切り替えができるようにインターフェイスを使えるようになってください。インターフェイスを使ったプログラミングの経験を積むと、自然と自作インターフェイスの設計ができるようになるはずです。インターフェイスを有効に活用できるようになると、「変更」に強いプログラミングができるようになります。ぜひ、積極的に使うようにしましょう。
インターフェイスを使ったプログラミングができるようになると、同じ名前のクラスを作りたくなることが多くなります。例えば、本当はDiceという名前のインターフェイスを今回作りたかったのですが、DiceIFという名前で作成しました。第7回のときにすでにDiceクラスというものを作成してしまっていたため、同じ名前となるDiceインターフェイスを作成できなかったからです。
こういった問題を解決するために、Javaには「パッケージ」という概念が導入されています。次回はこのパッケージについて解説する予定ですので、お楽しみに。
今回作ったサンプルのソースコードはこちらからダウンロードできます。
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)やbugs(J)の活動へも参加している。
著書に「基礎Java」(インプレス)、共著に「Javaコレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。
Copyright © ITmedia, Inc. All Rights Reserved.