多態性

<本章の目的>多態性を理解する。C++言語よりもかなり分かり易くなっています。

例題12-2-1 フィールド、メソッドをオーバーライドして下さい。

○プログラム

class Rectangle{
  int width;
  int height;
  double PI=3.14;
  void setSize(int width,int height){
    this.width=width;
    this.height=height;
  }
}

class NamedRectangle extends Rectangle{
  double PI=3.1415;
  double getSuperPI(){
    return super.PI;
  }
  void setSize(int width,int height){
    this.width=-width;
    this.height=-height;
  }
  public static void main(String[] args){
    NamedRectangle nr=new NamedRectangle();
    nr.setSize(123,45);
    System.out.println(nr.width);
    System.out.println(nr.height);
    System.out.println(nr.PI);
    System.out.println(nr.getSuperPI());
  }
}


○実行結果

D:\atsushi\Java\List12-8>java NamedRectangle
-123
-45
3.1415
3.14
-- Press any key to exit (Input "c" to continue) --

○解説

オーバーライドとはサブクラスで同じフィールド、同じメソッドを定義することです。
同じフィールドとは、型と名前が等しいこと、
同じメソッドとは、返却値型、メソッドの名前、引数列の数と型が等しいことを意味します。

オーバーライドすると上書きした新しいサブクラスのフィールド、メソッドが呼び出されます。
スーパークラスの古いフィールドを呼び出す為には super.PI のように、
スーパークラスの古いメソッドを呼び出す為には super.setSize(width,height) のように書きます。

フィールドをオーバーライドすることは、スーパークラスのフィールドを隠すことになります。
これを、フィールドのハイディング( hiding )と呼びます。

○補足

オーバーライド……上書き定義
オーバーロード……多重定義


○補足2

final フィールド、final メソッドはオーバーライド出来ません。
同様に、final クラスは継承できません。


例題12-2-2 オブジェクトの多態性を確認して下さい。

○プログラム

class Rectangle{
  int width;
  int height;
  void setSize(int width,int height){
    this.width=width;
    this.height=height;
  }
}

class NamedRectangle extends Rectangle{
  String name;
  NamedRectangle(String name){
    this.name=name;
  }
  void setSize(int width,int height){
    this.width=-width;
    this.height=-height;
  }
  public static void main(String[] args){
    NamedRectangle nr=new NamedRectangle("ATHENS");
    Rectangle r=nr;
    r.setSize(123,45);
    System.out.println(r.width);
    System.out.println(r.height);
    //System.out.println(r.name); //error
    System.out.println("-----");
    System.out.println(nr.width);
    System.out.println(nr.height);
    System.out.println(nr.name);
  }
}


○実行結果

D:\atsushi\Java\P62>java NamedRectangle
-123
-45
-----
-123
-45
ATHENS
-- Press any key to exit (Input "c" to continue) --

○解説

サブクラスのインスタンスは、スーパークラスのインスタンスに代入できます。
そして、スーパークラスのインスタンスとして扱うことが出来ます。
つまり、アクセスできるのはスーパークラスのフィールド、メソッドであり、
サブクラスのフィールドメソッドにはアクセスできません。
ただし、オーバーライドしたフィールド、メソッドは
上書きした新しいサブクラスのフィールド、メソッドにアクセスします。

例題12-2-3 サブクラスのインスタンスを代入されたスーパークラスのインスタンスを
          サブクラスにキャストして下さい。


○プログラム

class Rectangle{
  int width;
  int height;
  void setSize(int width,int height){
    this.width=width;
    this.height=height;
  }
}

class NamedRectangle extends Rectangle{
  String name;
  NamedRectangle(String name){
    this.name=name;
  }
  void setSize(int width,int height){
    this.width=-width;
    this.height=-height;
  }
  public static void main(String[] args){
    NamedRectangle nr=new NamedRectangle("ATHENS");
    Rectangle r=nr;
    r.setSize(123,45);
    System.out.println(r.width);
    System.out.println(r.height);
    if(r instanceof NamedRectangle){
      System.out.println(((NamedRectangle)r).name);
    }
    System.out.println("-----");
    System.out.println(nr.width);
    System.out.println(nr.height);
    System.out.println(nr.name);
  }
}


○実行結果

D:\atsushi\Java\P63>java NamedRectangle
-123
-45
ATHENS
-----
-123
-45
ATHENS
-- Press any key to exit (Input "c" to continue) --

○解説

r instanceof NamedRectangle

instanceof 演算子は、
r が NamedRectangle クラスのインスタンスを指していれば true を、
r が NamedRectangle クラスのインスタンスを指していなければ false を返します。

r は Rectangle クラスの変数ですが、
NamedRectangle クラスのインスタンスを指すので、
r instanceof NamedRectangle は true を返します。

多態性(ポリモルフィズム)

書籍「決定版 はじめてのC++(塚越一雄)」では以下のように述べています。
 一般にオブジェクト指向プログラミングというと、
 カプセル化によるデータの隠蔽や継承だけが強調される傾向にある。
 しかし、この多態性こそがオブジェクト指向プログラミングの威力が発揮される部分である。

いま私が読んでいる「Java言語プログラミングレッスン(結城浩)」では、
多態性の扱いはぞんざいですが、塚越一雄氏によれば重要らしい。
らしい……というのは、理由は分かるんだけど、使用経験がないから。

多態性の威力が発揮されるのは、例えば以下のようなプログラムです。

class A{
  ……
  show(){ …… }
}

class B extends A{
  ……
  show(){ …… }
}

class C extends A{
  ……
  show(){ …… }
  public static void main(String[] args){
    B b=new B();
    C c=new C();
    A a=b;
    a.show();
    a=c;
    c.show();
  }
}


一度目の a.show() は B クラスの show() を、
二度目の a.show() は C クラスの show() を呼び出します。
このように、全く同じプログラムにもかかわらず、
代入されているインスタンスにより、その振る舞いが変わります。

これが、多態性の意味です。

C++言語ではややこしく感じた多態性ですが、
Java言語では細かいところが改善され、
直感的で、かなり分かり易くなっていると思います。

例題12-2-4 アクセス制御を行って下さい。

○プログラム

class Rectangle{
  private int width;
  private int height;
  int getWidth(){
    return width;
  }
  int getHeight(){
    return height;
  }
  void setSize(int width,int height){
    this.width=width;
    this.height=height;
  }
}

class NamedRectangle extends Rectangle{
  void method(){
    setSize(123,456);
    System.out.println(getWidth());
    System.out.println(getHeight());
  }
  public static void main(String[] args){
    //setSize(123,456); //error
    //System.out.println(getWidth()); //error
    //System.out.println(getHeight()); //error
    NamedRectangle nr=new NamedRectangle();
    nr.method();
  }
}


○実行結果

D:\atsushi\Java\List12-10>java NamedRectangle
123
456
-- Press any key to exit (Input "c" to continue) --

○解説

private 修飾子を付けて宣言したフィールドやメソッドは、他のどのクラスからも参照できません。
アクセス制御の詳細は「パッケージ」を参照して下さい。
また先延ばしかよ!って感じですが、何かちゃんとした理由があるんでしょう、きっと、たぶん……。
どうもこの本は進むのが遅いですね。

private を省略すると public と見なされるようですが、詳細は不明です。
しかし、フィールドは private にするのが、カプセル化の基本です。

例題12-2-5 抽象メソッドを宣言して下さい。

○プログラム

abstract class Player{
  public abstract void play();
  public void loop(int n){
    for(int i=0;i<n;i++){
      play();
    }
  }
}

public class TextPlayer extends Player{
  String text;
  public TextPlayer(String text){
    this.text=text;
  }
  public void play(){
    System.out.println(text);
  }
  public static void main(String[] args){
    TextPlayer player=new TextPlayer("こんにちは");
    player.loop(3);
  }
}


○実行結果

D:\atsushi\Java\List12-12>java TextPlayer
こんにちは
こんにちは
こんにちは
-- Press any key to exit (Input "c" to continue) --

○解説

本体がないメソッドのことを抽象メソッドと呼びます。
メソッド本体とは { と } でくくられた部分のことです。
抽象メソッドとする為には、本体を書かないこと、
更に、abstract を付けて宣言すること、が必要です。


抽象メソッドをもつクラスを抽象クラスと呼びます。
抽象クラスにも abstract を付けて宣言しなければなりません。
抽象クラスのインスタンスは作れません。

抽象メソッドはオーバーライドされることを期待しています。
抽象メソッドをオーバーライドしない限り、そのクラスも抽象クラスになります。

○補足

C++言語では多態性で(純粋)仮想関数を必要としました。
純粋仮想関数を持つクラスが抽象クラスでしたね。
しかし、Java言語では多態性に抽象メソッドは不要なので、
あまり活躍の場は無いように思われます。
ただサブクラスに、どのようなメソッドを宣言しなければならないかということを強要しているだけです。
利点はサブクラスのメソッドの名前が揃う為に、
プログラムが読みやすくなることぐらいでしょうか。


戻る / ホーム