インタフェース

<本章の目的>インタフェースを習得する。インターフェイスと言った方が分かり易いかも?

例題14-1 フィールドとメソッドを持つインタフェースを宣言して下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}


○解説

インタフェース( interface )は抽象クラスに似ています。
抽象クラスはサブクラスに、どのようなメソッドを宣言しなければならないかということを強要していました。
インタフェースも同様に、それを実装したクラスに対して共通の雛形、操作性を提供することを目的としています。
具体的にどのような処理を行うかは実装したクラスに委ねられますが、
とりあえず、インタフェースにあるフィールドとメソッドは既知の名前で呼び出すことが出来ますよね。

インタフェースの特徴をまとめます。

インタフェースは参照型の一種です。
・インタフェースが持つフィールドは必ず定数です。
・インタフェースが持つメソッドは必ず抽象メソッドです(つまりメソッドは実装されていません)。
・インタフェースはインスタンスを作ることは出来ません

interface という予約語でインタフェース宣言の始まりを表します。
それにつづく単語 Math がインタフェースの名前ですね。

double PI=3.14;

インタフェースで宣言されているフィールドは、自動的に public static final になります。
つまり、public static final double PI=3.14; と宣言したのと同じです。

void show();

インタフェースで宣言されているメソッドは、自動的に public abstract になります。
つまり、public abstract void show(); と宣言したのと同じです。

例題14-2 インタフェースを実装して下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}

class Circle implements Math{
  double r;
  public Circle(double r){
    this.r=r;
  }
  public void show(){
    System.out.println("面積="+PI*r*r);
  }
  public static void main(String[] args){
    Circle c=new Circle(12.3);
    System.out.println("円周率="+PI);
    System.out.println("半径="+c.r);
    c.show();
  }
}


○実行結果

D:\atsushi\Java\P108>java Circle
円周率=3.14
半径=12.3
面積=475.0506000000001
-- Press any key to exit (Input "c" to continue) --

○解説

implements という予約語でインタフェースを実装します。
実装とは、メソッドの本体を定義することを意味します。
継承と同じで、実装したクラスはインタフェースの全てのフィールドとメソッドを持つことになります。

interface Math{
  double PI=3.14;
  void show();
}


は、

interface Math{
  public static final double PI=3.14;
  public abstract void show();
}


と同じでしたから、
static フィールド PI にはインスタンスなしで直接アクセスできますし、
 → System.out.println("円周率="+PI);
非 static メソッド show にはインスタンスを通して間接アクセスします。
 → c.show();

○補足

3.14*12.3*12.3 の正確な計算結果は 475.0506 です。
実行結果では 475.0506000000001 となっているので、
精度は最高でも小数点以下12桁ということになりますね。

例題14-3 クラスの継承とインタフェースの実装を同時に行って下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}

class Point{
  int x,y;
}

class Rect extends Point implements Math{
  int width,height;
  public Rect(int x,int y,int width,int height){
    this.x=x;
    this.y=y;
    this.width=width;
    this.height=height;
  }
  public void show(){
    System.out.println("(x,y)=("+x+","+y+")");
    System.out.println("(幅,高さ)=("+width+","+height+")");
    System.out.println("面積="+width*height);
  }
  public static void main(String[] args){
    Rect r=new Rect(6,4,3,2);
    r.show();
  }
}


○実行結果

D:\atsushi\Java\P110>java Rect
(x,y)=(6,4)
(幅,高さ)=(3,2)
面積=6
-- Press any key to exit (Input "c" to continue) --

○解説

例題14-2と同様にインタフェースを実装していますが、その処理内容は異なります。
さらに Circle クラスと Rect クラスは階層も違います
(インタフェースはクラス階層に含まれません)。

Object
 └- Circle

Object
 └- Point
   └- Rect

それでも、同様のメソッド show で、インスタンスの情報を表示することは同じです。
インタフェースを実装することで同じ操作性が得られたのですね。

スーパークラスにインタフェースと同じメソッドがあったら?

interface Interface{
  void show();
}

class Super{
  public void show(){
  }
}

class Sub extends Super implements Interface{
  public static void main(String[] args){
  }
}


これもまた、実装したと見なされます

D:\atsushi\Java\P110-2>java Sub
-- Press any key to exit (Input "c" to continue) --

例題14-4 インタフェース型の変数を宣言して下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}

class Circle implements Math{
  double r;
  public Circle(double r){
    this.r=r;
  }
  public void show(){
    System.out.println("面積="+PI*r*r);
  }
  public static void main(String[] args){
    Math m=new Circle(12.3);
    System.out.println("円周率="+PI);
    System.out.println("半径="+((Circle)m).r);
    m.show();
  }
}


○実行結果

D:\atsushi\Java\P114>java Circle
円周率=3.14
半径=12.3
面積=475.0506000000001
-- Press any key to exit (Input "c" to continue) --

○解説

インタフェースはインスタンスを作ることが出来ません。
つまり、Math m=new Math(); のようにすることは出来ません。
しかし、インタフェースの変数にそれを実装したクラスのインスタンス(のポインタ)を代入することは可能です
これは多態性でやりましたね。

System.out.println("半径="+((Circle)m).r);

r は Circle クラスのフィールドなので、
通常は Math インタフェースの変数 m からアクセスすることは出来ません。
しかし、m には Circle クラスのインスタンス(のポインタ)が代入されています。
したがって、キャストすれば、Circle クラスにもアクセスすることが出来ます。

m.show();

show は Math インタフェースの(抽象)メソッドです。
したがって、Math インタフェースの変数 m からアクセスできます。
ただし、オーバーライドされているので実際に呼び出されるのは Circle の show メソッドです。

ここでも重要なのは、同じ操作性が提供されているということです。
とりあえず show メソッドを呼び出せば、インスタンスの情報を表示させることが出来ます。

例題14-3の Rect クラスも Math m=new Rect(6,4,3,2); とすることが出来ます。

インタフェースはインスタンスを作ることは出来ませんが、配列を作ることは出来ます

Math[] m=new Math[2];
m[0]=new Circle(12.3);
m[1]=new Rect(6,4,3,2);

for(int i=0;i<m.lengh;i++){
  m[i].show();
}


こうすれば、同じ操作を繰り返す(ループ)だけで、
すべてのインスタンスの情報を表示させることが出来ます。

○補足

Math m=new Circle(12.3);
System.out.println("半径="+((Circle)m).r);

このようなキャストが許されるのは、Math インタフェースの変数 m に、
それを実装するクラス Circle のインスタンス(のポインタ)が代入されているからです。
もし仮にそうでないのならば、ClassCastException という例外がスローされます。

例題14-5 引数がインタフェースのメソッドを作って下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}

class Circle implements Math{
  double r;
  public Circle(double r){
    this.r=r;
  }
  public void show(){
    System.out.println("面積="+PI*r*r);
  }
  static void func(Math m){
    System.out.println("円周率="+PI);
    System.out.println("半径="+((Circle)m).r);
    m.show();
  }
  public static void main(String[] args){
    Circle c=new Circle(12.3);
    func(c);
  }
}


○実行結果

D:\atsushi\Java\P116>java Circle
円周率=3.14
半径=12.3
面積=475.0506000000001
-- Press any key to exit (Input "c" to continue) --

○解説

メソッドの引数としてインタフェースを使うことも出来ます。

Circle c=new Circle(12.3);
func(c);


は、

Math m=new Circle(12.3);
func(m);


としても同じです。

例題14-6 インタフェースを継承して拡張したインタフェースを作って下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}

interface Physics extends Math{
  double G=9.8;
}

class High implements Physics{
  double time;
  public High(double time){
    this.time=time;
  }
  public void show(){
    System.out.println("落下高度="+0.5*G*time*time);
  }
  public static void main(String[] args){
    High h=new High(5.43);
    System.out.println("重力加速度="+G);
    System.out.println("落下時間="+h.time);
    h.show();
  }
}


○実行結果

D:\atsushi\Java\P119>java High
重力加速度=9.8
落下時間=5.43
落下高度=144.47601
-- Press any key to exit (Input "c" to continue) --

○解説

インタフェースを継承して出来るのは、やっぱりインタフェースです。
class High extends Physics のようにすることは出来ません。

Math は Physics のスーパーインタフェースです。
Physics は Math のサブインタフェースです。

○補足

クラスの継承においてスーパークラスは一つだけしか指定できませんでしたが、
インタフェースの継承においては、スーパーインタフェースは複数指定可能です。

例題14-7 インタフェースを多重実装して下さい。

○プログラム

interface Math{
  double PI=3.14;
  void show();
}

interface Physics{
  double G=9.8;
}

class High implements Math,Physics{
  double time;
  public High(double time){
    this.time=time;
  }
  public void show(){
    System.out.println("落下高度="+0.5*G*time*time);
  }
  public static void main(String[] args){
    High h=new High(5.43);
    System.out.println("重力加速度="+G);
    System.out.println("落下時間="+h.time);
    h.show();
  }
}


○実行結果

D:\atsushi\Java\P120>java High
重力加速度=9.8
落下時間=5.43
落下高度=144.47601
-- Press any key to exit (Input "c" to continue) --

○解説

implements の後に , で区切ってインタフェースを並べれば、
いくらでもインタフェースを多重実装(こんな言葉はないかも)することが出来ます。

Java言語はクラスを多重継承することが出来ません。
段階的に継承することは出来ても、スーパークラスは必ず一つと決まっています。
コレ!と根本になるクラスを決めたら、後は自分で地道に拡張していくしかないのです。
途中で、違うクラスを継承したくなっても不可能なのです。
C++言語では多重継承できたのに、不便ですね。
しかし、インタフェースを使えば擬似的に多重継承することが出来ます。
ただし、インタフェースを実装するのは自分ですから
既存のメソッドの恩恵を受けることは出来ません。
あまり意味がないような気がするのは私だけ……?

しかし、最初にも言いましたが、
インタフェースはそれを実装したクラスに対して共通の雛形、操作性を提供することを目的としています。
つまり、共通のフィールド、メソッドを持っているということをプログラムで示すことに意味があるのです。
多態性も重要です。
操作性は共通ですが、それを実装するクラスによって様々に振る舞います。
抽象クラスやインタフェースを上手く使えたなら
多態性がその実力を発揮できるかも知れませんね。

クラスとインタフェースの比較

クラス インタフェース
インスタンス 作れる 作れない
メソッド いろいろ 必ず public abstract
フィールド いろいろ 必ず public static final
スーパークラス 1つだけ 持てない
スーパーインタフェース 複数指定可能
( implementgs を使う)
class A implements B,C{
 ……
}
複数指定可能
( extends を使う)
interface X extends Y,Z{
 ……
}

戻る / ホーム