インターフェイス(Java)
インターフェイス
はじめに
昔(1990年代後半くらい)に出版された書籍の中では「Javaは多重継承ができないので、Interfaceという多重継承の仕組みをもっている」などど書いてあるものがありました。そのため、Javaを始めた当初は「なんでこんなものがあるの」という感覚をもっていました。ですが、上に書いたようなインターフェイスの存在意義は「(その書籍の著者の)勘違い」か「説明不足」で、インターフェイスは、英語をそのまま解釈して「他のクラスとのインターフェイスを定義するもの」と解釈するのがもっとも適当であると、思っています。私がこのように解釈がはじめてできたのは、デザインパターンについて勉強してみたときでした。最近では、多くの書籍が出版されていますので、1つくらい読んで見ると「目からうろこがおちる」のではないかと思います。
最近では、AOP(Aspect Oriented Programing;アスペクト指向プログラミング」や「ORマッピング」に絡んで「DAOパターン」といったインターフェイスをつかったプログラムが見直されています。
とはいえ、なかなか「感じ」がつかめないと思いますので、1つの例題を中心に「インターフェイスってこんなものなんだ」、「こんなことができるんだ」ということを実感してもらうことを目標に書きたいと思います。
インターフェイス
インターフェイスとは、上に書きましたように「他のクラスとのインターフェイスを定義するもの」です。インターフェイスは継承をすることができ、親子の関係をつくることができます(また、その際のルールはクラスと同じです)。また、クラスでは「単一継承」しかできませんでしたが、インターフェイスは複数をつかうことができます。インターフェイスを使うことをimplementする(まさに、実装するといいます)。インターフェイスはクラスの宣言部で、「このインターフェイスを実装しますよ」という形式で宣言します。形式は以下です。
修飾子 class クラス名 extends 親クラス名 implements インターフェイス名1,インターフェイス名2{
…
}
また、インターフェイスそのものは、以下のような形式をしています。
修飾子 interface インターフェイス名 extends 親インターフェイス名{
…
}
インターフェイスはあくまで、「他のクラスとのインターフェイス(つまり、メソッド)」を規定するもので、その中にはメソッドのシグナチャだけを宣言し、メソッドの中身は実装しません。インターフェイスをimplementsしたクラスは、インターフェイスにあるすべてのメソッドの実装(implement)をする必要があります。
今回は、これまで使ってきたActorをクラスからインターフェイスを使った実装に変更し、そのインターフェイスをimplementsした何人かの登場人物を登場させて台詞を言わせたいと思います。
まずは、test.basic.beanにActorInterfaceを作ってみましょう。
ActorInterface.javaは次のようにしてください。
package test.basic.bean;
public interface ActorInterface {
public String action();
public void setWords(String str);
public String getName();
}
これは、Actorにimplementsするインターフェイスなので、action()やsetWord()(台詞をセットする)といったメソッドを持ちます。また、役ごとに実装クラスを持つようにしますので、getName()でその役をとることにします。
さぁ、なにをやるかというところですが、てもとに「ハムレット」があったので、その中の一部(第3幕第2場黙劇後)からとってみることにしました。役者はハムレット(hamlet)とオフェリア(ophelia)、旅芸人の役者(Somebody)の3人です。また、舞台の様子を表現する部分があるので、それは黒子(Nobody)に語ってもらうことにしました。
これらから、test.basic.beanに以下のようにつくりました。これらは、基本的にnameというプロパティ値とコンストラクタ以外全て同じです。また、メソッドもActorInterfaceで規定されているもの以外は実装していません。
1.HamletImpl
package test.basic.bean;
public class HamletImpl implements ActorInterface{
private String name = "Hamlet";
private String words;
// デフォルトのコンストラクタ
public HamletImpl(){
}
public String action() {
return words;
}
public void setWords(String string) {
words = string;
}
public String getName() {
return this.name;
}
}
2.OpheliaImpl
package test.basic.bean;
public class OpheliaImpl implements ActorInterface{
private String name = "Ophelia";
private String words;
// デフォルトのコンストラクタ
public OpheliaImpl(){
}
public String action() {
return words;
}
public void setWords(String string) {
words = string;
}
public String getName() {
return this.name;
}
}
3.SombodyImpl
package test.basic.bean;
public class SomebodyImpl implements ActorInterface{
private String name = "Actor";
private String words;
// デフォルトのコンストラクタ
public SomebodyImpl(){
}
public String action() {
return words;
}
public void setWords(String string) {
words = string;
}
public String getName() {
return this.name;
}
}
4.NobodyImpl
package test.basic.bean;
public class NobodyImpl implements ActorInterface{
private String name = " ";
private String words;
// デフォルトのコンストラクタ
public NobodyImpl(){
}
public String action() {
return words;
}
public void setWords(String string) {
words = string;
}
public String getName() {
return this.name;
}
}
次に、これらを演じてもらうmainメソッドを用意する必要があります。test.basicにSample021を作成し、以下のようにコードをいれてみてください。
package test.basic;
import java.util.ArrayList;
import java.util.Iterator;
import test.basic.bean.ActorInterface;
import test.basic.bean.HamletImpl;
import test.basic.bean.NobodyImpl;
import test.basic.bean.OpheliaImpl;
import test.basic.bean.SomebodyImpl;
public class Sample021 {
public static void main(String[] args) {
ArrayList aList = new ArrayList();
// 台詞の設定
ActorInterface o1 = new OpheliaImpl();
o1.setWords("ハムレット様、あれはどういう意味でございます?");
aList.add(o1);
ActorInterface h1 = new HamletImpl();
h1.setWords("いや、それ、あれはいかさま、いわば意味なきいたずら。");
aList.add(h1);
ActorInterface o2 = new OpheliaImpl();
o2.setWords("どうやらお芝居のあらすじらしゅうございます。");
aList.add(o2);
ActorInterface n1 = new NobodyImpl();
n1.setWords("まくの前にひとりの役者が現れる。王と妃、それに注意する。");
aList.add(n1);
ActorInterface h2 = new HamletImpl();
h2.setWords("こいつの話で万事が分かる。役者に秘密は守れない。何もかも喋ってしまうぞ。");
aList.add(h2);
ActorInterface o3 = new OpheliaImpl();
o3.setWords("いま見せてくれたお芝居の意味も?");
aList.add(o3);
ActorInterface h3 = new HamletImpl();
h3.setWords("(荒々しく)もちろんだ、そのほか何でも教えてくれよう、おまえのほうから見せてやりさえすれば \n"+
"片端からな−−お前さえ恥ずかしがらずに見せてやれば、やつも恥じずにこれはこうと教えてくれるものだ");
aList.add(h3);
ActorInterface o4 = new OpheliaImpl();
o4.setWords("また、そのような、私はお芝居を見ることにいたします。");
aList.add(o4);
ActorInterface s1 = new SomebodyImpl();
s1.setWords("われら一座のため、また、ここに演じます悲劇のため、寛大なるご見物のみなさまがた\n"+
"なにとぞ多少の不出来はお許しくださいますよう。(退場)");
aList.add(s1);
ActorInterface h4 = new HamletImpl();
h4.setWords("あれは、口上か指輪の銘か。");
aList.add(h4);
ActorInterface o5 = new OpheliaImpl();
o5.setWords("本当にみじこうございますこと。");
aList.add(o5);
ActorInterface h5 = new HamletImpl();
h5.setWords("女の恋のように。");
aList.add(h5);
// アクション
Iterator it = aList.iterator();
while(it.hasNext()){
ActorInterface tmp = (ActorInterface)it.next();
String name = tmp.getName();
String action = tmp.action();
System.out.println(name + ";" + action);
System.out.println("\n");
}
}
}
それではこれを実行してみてください。以下のように出力されましたか?
Ophelia;ハムレット様、あれはどういう意味でございます?
Hamlet;いや、それ、あれはいかさま、いわば意味なきいたずら。
Ophelia;どうやらお芝居のあらすじらしゅうございます。
;まくの前にひとりの役者が現れる。王と妃、それに注意する。
Hamlet;こいつの話で万事が分かる。役者に秘密は守れない。何もかも喋ってしまうぞ。
Ophelia;いま見せてくれたお芝居の意味も?
Hamlet;(荒々しく)もちろんだ、そのほか何でも教えてくれよう、おまえのほうから見せてやりさえすれば
片端からな−−お前さえ恥ずかしがらずに見せてやれば、やつも恥じずにこれはこうと教えてくれるものだ
Ophelia;また、そのような、私はお芝居を見ることにいたします。
Actor;われら一座のため、また、ここに演じます悲劇のため、寛大なるご見物のみなさまがた
なにとぞ多少の不出来はお許しくださいますよう。(退場)
Hamlet;あれは、口上か指輪の銘か。
Ophelia;本当にみじこうございますこと。
Hamlet;女の恋のように。
「台詞の設定(ながくなって恐縮です)」は本質的ではありません。アクションのところに注目してください。台詞の設定でArrayListにいれた役者(実装クラス)は4種類ありましたが、(if文を含まずに)1つのループで全ての役者が台詞を言うことができました。「こういうコーディングができるんだぁ」と思われた方もいらっしゃると思います。ポイントは「インターフェイスでメソッドを規定したこと」、「(実装クラスではなく)インターフェイスを型として用いたこと」です。この2つによって、異なる実装クラスが1つのインターフェイスを通じて、まったく同じに扱われていることが分かります。役者が増えたとしてもアクションの部分に変更はいりません。
インターフェイスをうまく使えるか、否かでシステムの設計が大きく変わります。いきなりは無理でしょうから、はじめの内は、実装クラスがなんとなく似ているときに「メソッドを(抽象化したら)共通化できるかな」といった視点で見ていければ、グッドなのではないかと思います。
2007年02月26日(月) 15:40:00 Modified by wanderingse