Objects First With JAVA using BlueJ

Chapter 5 Interface

★キーワード(Keywords)

interface インターフェース
implements インプリメンツ
polymorphism ポリモーフィズム(多形性)
access modifier アクセス修飾子
static スタティック(静的な、定位の)

★インターフェースとは?

 インターフェースはインヘリタンスと並んでオブジェクト指向の大事な概念の一つです。ちょっと響きも似ているので混同しがちなんですが(‥え?そんな間抜けは私だけ??)激しく違います。 何が違うのかというと、そもそもの役目が違います。

 インターフェースは「クラスが何をするのか」を定義し、それを「どう」実装するかはそれぞれのクラスに任せています。 簡単に言うと、「こういうことをしたいなぁ。」というだけで、「それじゃーこういう方法でやりましょう。」というトコは関知しないという感じです。 どういう方法で実行する(implement)かはそのインターフェースを実装するクラスで決めます。 Javaではインターフェースはクラスではありません。クラスへの要求のセットです。 インターフェースはAbstract methodだけを持ちます。(メソッド宣言だけ、中身が無いメソッド→参照:四章Abstract(抽象)ってなに?へ。

★‥してそのメリットは?

 これでは何のためにインターフェースがあるのか、イマイチはっきりしませんね。 前回出てきたInheritanceに関連があります。何故なら一つのクラスは一つしか他のクラスをInheritすることが出来ません。つまり一つのSubClassは一つのSuperClassしか持てません。これはJavaの言語規制です。

 つまり、複数のクラスをExtendsした機能たっぷりなクラスは出来ません、ということです。 なんだケチくさい‥という訳ではありません。これはJavaのよりsimpleに!という心意気に基づいています。 SuperClass(親)は複数のSubClass(子)を持つことが出来ますが、逆はダメです。親子関係のTreeを思い浮かべるとわかりやすいと思います。

 私が聞いて「これはわかりやすい」と思った例を挙げておきます。 あるClassでメソッドを呼んだとき、Javaではまずそのクラスの中にメソッドがあるかどうかを探します。なかった場合、すぐ上のSuperClassに探しに行きます。そこにもなかったら、もう一個上‥継承関係を遡る形で探しに行きます。 どこか途中で発見した時点でそのメソッドを使います。 このとき、一つのクラスが複数の親を持っていた場合、道が二つ以上になってしまう、といえます。 これはコンパイラにとっても複雑だし、そのプログラムを扱ってるプログラマにとってもややこしいことこの上ないのです。

 代わりに登場するのがインターフェースです。 クラスは幾らでも好きなだけインターフェースをimplementsすることが出来ます。 しかし当然ながら、implements(実装)しなければいけません。 インターフェースはメソッドの名前だけを持っているので、その中身をクラスが実装します。

 こう聞くと、実際のコードを書くのはそのクラスの中なんだから、わざわざインターフェースを使うのは意味がない様な感じがします。(←ていうかそう思いました。私は‥(^^;)) これにはオブジェクト指向のもう一つの重要な考え方、Polymorphism(ポリモーフィズム)に関係してきます。

★Polymorphism(ポリモーフィズム)とは?

 さて、ここは大事なので段落を分けました(笑) Polymorphismを日本語に訳すと「多形性、同質異像」などと訳せます。 これは「もともとは同じモノだったのが、ちょっと違う形に変化したんだよ」ということです。(←良いのか?こんなんで‥) これは実はInheritanceの回で書いておくべきだったのかも知れないんですが‥‥ちょっと遅くなってしまいました。。

 Inheritanceでは、あるクラス(SuperClass)をextendsして、新しいクラス(SubClass)を作るわけなんですが。 これは「最初のクラスの機能を持ってる、ちょっと形の違う新しいクラス」であるとも言えます。 つまりこれがPolymorphismです。

 クラスがPolymorphismであるメリットは、4章で出てきた、AnimalとHospitalの例をまた使って説明します。 今回は細かいコードは書かずに継承関係だけを図で表します。

クラス図1

 この図は、クラス図のつもりで書いています。上からクラス名、フィールド、メソッドを表示してあります。(上図の一つだけある灰色の長方形がそれを説明しています)

 このクラス図では、Animal, Cat, Dog, Hospitalという4つのクラスの関係を示しています。 Animalは前回同様Abstract Classとします。 AnimalとCatは継承関係AnimalとDogも継承関係にあります。 AnimalとHospitalは参照関係にあります。

 ここで、CatクラスとDogクラスはAnimalクラスの変形型だといえます。これがPolymorphismです。 Hospitalクラスはcage[]というフィールド値を持ちます。このときcage[]はAnimal型の配列(array)であることを想定しているとします。(この辺りは4章で使った例とほとんど一緒です。)

 Hospitalのcageに入るのはAnimalをextendsしているクラスのオブジェクトです。 つまりこの場合CatかDogクラスのオブジェクトです。 cageに入るオブジェクトは実際CatクラスかDogクラスのインスタンス化されたオブジェクトですが、それはAnimal型として一元管理されます。(AnimalクラスはAbstract classなので直接インスタンス化することは出来ませんが) こうすることでHospital側では、わざわざCat用、Dog用というように異なるcageを作らなくて済むことになります。 これはPolymorphismのメリットです。 更にInheritanceだけでなく、インターフェースでも同じ事が出来ます。

★インターフェースとアブストラクトクラス

 インターフェースはAbstract methodを持つ、と前述しましたが、4章で出てきたAbstract ClassもAbstract methodを持つことが出来ます。 両者の違いは何なのか、というと。。

 簡単に言うと、インターフェースはクラスではない、というのが一番大きいと思います。 そのままじゃないか、という気もしますが‥(-_-;) インターフェースを使うと、そのクラスに他にクラスをextendsする余地を残せる、ということです。 クラス=実体、インターフェース=機能といったところでしょうか。。

★Access Modifier(アクセス修飾子)

 2章で少し説明しましたが、ここでもう一度アクセス修飾子について書いておきます。 アクセス修飾子とは、クラスやメソッドの可視性(access visibility)を決めるものです。

private そのクラスの中でのみ使用可。
protected そのクラスの含まれるパッケージと全てのサブクラスから使用可。
public どこからでも自由に使用可。
(default) そのクラスの含まれるパッケージで使用可。

 クラスのインスタンスフィールドが普通privateである、というのはこのためです。 publicやprotectedにした場合、そのクラス以外のクラスからアクセスがあって、そのクラスが知らない間に値が変えられてしまうかもしれないからです。 4つ目のdefaultというのは、何も書かなかった場合です。

 しかし、インターフェースでは、methodのdefault access modifierはpublicです。これはインターフェースがpublicメソッドしか持てない、と決まっているからです。 なのでインターフェースのメソッドをクラスでimplementsするときは、publicと書き足さなければいけません。 何故ならクラスでのデフォルトアクセス修飾子では、そのメソッドはパッケージのvisibilityしか持たないからです。

  更にインターフェースはインスタンスフィールドを持つことが出来ません。これはインターフェースがクラスではないためインスタンス化することが出来ないからです。 しかし、インターフェースはpublic static finalなフィールド値を持つことが出来ます。 これはインスタンスフィールドではなく、定数のようなモノと考えることが出来ます。

★Staticって?

 staticとは、「静的な、定位の」などと訳せます。つまり「動かないモノ」というわけですね。 Javaでstaticというと、「クラスに属するもの」といって良いと思います。 普通、メソッドやフィールドはインスタンス化することでそれぞれの仕事をしますが、staicなメソッドやフィールドはインスタンス化されたオブジェクト毎に変化するモノではなくなります。 最初に定義されているクラスに属しているのです。

 ちょっとわかりにくいので例としてサンプルプログラムを用いてみます。

public class Radio{

  private static int nextNum = 0;  //staticフィールド
  private int serialNum;       //インスタンスフィールド
  private double price;
  private String type;

  public Radio(){  //コンストラクタ
    type = "";
    price = 0;
    setSerialNum();
  }

  public void setPrice(double n){
    price = n;
  }

  public double getPrice(){
    return price;
  }

  public void setType(String s){
    type = s;
  }

  public String getType(){
    return type;
  }

  private void setSerialNum(){   //privateメソッド
    serialNum = nextNum;    
    nextNum ++;
  }

  public static int getNextNum(){  //staticメソッド
    return nextNum;
  }

}

public class StaticTest{

  public static void main(String[] args){
    Radio radio1 = new Radio();  //Radioをインスタンス化
    radio1.setPrice(1000);
    radio1.setType("Sanyo");

    Radio radio2 = new Radio();  //Radioをインスタンス化
  }

}

 さて。なんかこのサンプルプログラム、無駄に長い様な気もしますが‥‥(まあいいや)

 staticなメソッドはクラスをインスタンス化することなく呼ぶ事が出来ます。 そしてstaticメソッドからはinstanceフィールドは呼べません。staticフィールドだけです。

 StaticTestクラスの方を見てみましょう。 まずradio1というインスタンスをnewしています。その出来たインスタンスの値をセットしています。 これでradio1のフィールド値は、price = 1000; type = "Sanyo"; serialNum = 0; です。

 次にradio2をnewしています。 ここで、フィールド値は何もセットされていません。 因ってradio2のフィールド値は、price = 0; type = ""; serialNum = 1; です。(呼ばれたのはコンストラクタだけ)

 このサンプルプログラムは簡単なので、赤い字のキーワードにちょっと気を付ければ意味がわかると思います。

★最後に

 さて、今回はインターフェースのつもりだったのですが、なんだか半分くらいからインターフェースと関係ない方向へずれてしまった様な気もするんですが‥‥(タイトル変えた方が良いかな‥)

 


ああ疲れた一休みする。   とりあえず私的理解Javaのトップページに戻る  そのまま勉強続ける。