スポンサードリンク
HOME > Java > 基本・派生クラス間の参照の相互変換

Java - 基本・派生クラス間の参照の相互変換

クラス型変数は、それと同じクラス型の参照値を代入できます。

クラス型変数に、クラス型変数と違うクラス型の参照値は代入できません。

例を見てみましょう。

classA objA1 = new classA();   // OK
classA objA2 = new classA();   // OK
classB objB = new classB();   // OK

objA1 = objA2;          // OK
objA1 = objB;          // エラー


1つ目は、classA型の変数に、classAの参照が代入されていますので
問題はありません。

一方、2つ目は、classA型の変数に、classB型の参照が代入されていますので、
エラーとなり、実行できません。

これらは当然の事です。

しかし、基本クラスとその派生クラスでは、共通する部分があるため、

基本クラス型変数 = 派生クラス型変数(派生クラスの参照値)

と言った代入が許されています。

これは、派生クラスの参照値が代入されると、

基本クラス型として扱われるのですが、派生クラスはその中に基本クラスを

含んでいるので、全ての基本クラスメンバにアクセスできるためです。

代入後は、基本クラス型として働くので、

基本的には、派生クラスメンバへアクセスできません。


 ちなみに、その逆の

派生クラス型変数 = 基本クラス型変数(基本クラスの参照値)

と言った代入は許されていません

これは、基本クラスの参照値が代入されると、

派生クラス型として扱われるのですが、参照値そのものは、

基本クラスを指しているので、派生クラスメンバにアクセスできないからです。



 以上の説明で

基本クラス型変数 = 派生クラス型変数(派生クラスの参照値)

と言う代入ができる事が分かりました。

ここで1つ次のプログラムを見てみましょう。

プログラム例

ファイル名:Cmain.java
class A {
   public int data1;

   public void show_data1() { }  // 関数内容は省略

}

class B extends A {
   public int data2;

   public void show_data2() { }  // 関数内容は省略

}

public class Cmain {

   public static void main(String[] args) {
      A objA = new A();
      B objB = new B();

      
objA = objB;
//     
objB = objA;      // エラー

      objA.data1 = 10;    // OK
      objA.show_data1();   // OK
//     objA.data2 = 10;    // エラー
//     objA.show_data2();   // エラー

   }
}


このプログラム例では、

基本クラス型変数 = 派生クラス型変数(派生クラスの参照値)

の代入が行われています。逆は無理です。

代入後は、基本クラス型として働くので、

基本的には、派生クラスメンバへアクセスできません。


と説明した通り、派生クラスのdata2show_data2にはアクセスできません。



 次に、オーバーロードしたメンバ

オーバーライドしたメンバへのアクセスはどうなるか見てみましょう。

プログラム例

ファイル名:Cmain.java
class A {
   public
int data;

   public void
show() { }
   public void
func() { }
   public void
func(int a) { }  // 関数のオーバーロード

}

class B extends A {
   public
int data;     // 変数のオーバーライド

   public void
show() { }  // 関数のオーバーライド

   public void
func(int a, int b) { } // 関数のオーバーロード

}

public class Cmain {

   public static void main(String[] args) {
      A objA = new A();
      B objB = new B();

      objA = objB;

      objA.data = 10;  // 基本クラスのdataへアクセス
      objA.show();   //
派生クラスのshowへアクセス
      objA.func();   // 基本クラスのfunc()へアクセス
      objA.func(5);   // 基本クラスのfunc(int a)へアクセス
//     objA.func(5, 10); // エラー

   }
}


このプログラムで気になるところは、

派生クラスのオーバーライドした関数にアクセスできている事です。

基本クラス型変数 = 派生クラス型変数(派生クラスの参照値)

代入後は、基本クラス型として働くので、

基本的には、派生クラスメンバへアクセスできません。


と、説明しましたが、オーバーライドした関数へはアクセス可能となります。

逆にオーバーライドされた基本クラスの関数へは、アクセス不可となります。

 このように、オーバーライドした関数と基本クラスのオーバーライドされた関数の

どちらを呼ぶか決定する処理動的結合と言います。



次は、キャストを使って、派生クラスメンバにアクセスする例を見てみましょう。

プログラム例

ファイル名:Cmain.java
class A {
   public int data1;

   public void show_data1() { }  // 関数内容は省略

}

class B extends A {
   public int data2;

   public void show_data2() { }  // 関数内容は省略

}

public class Cmain {

   public static void main(String[] args) {
      A objA = new A();
      B objB = new B();

      objA = objB;

      objA.data1 = 10;    // OK
      objA.show_data1();   // OK
//     objA.data2 = 10;    // エラー
//     objA.show_data2();   // エラー

      (
(B)objA).data2 = 10;     // エラーにならない
      (
(B)objA).show_data2();    // エラーにならない

   }
}


この例では、

基本クラス型変数 = 派生クラス型変数(派生クラスの参照値)

の代入が行われた後、キャストを使って派生クラスメンバにアクセスしている例です。

代入しても、objAには、依然としてクラスBの参照値が格納されているので、

キャストすれば、通常通り、全ての派生クラスメンバにアクセス可能となります。



最後に関数の仮引数として基本クラス型の変数を指定する例を見てみましょう。

ファイル名:Cmain.java
class A {

   public void show() { }  // 関数内容は省略

}

class B extends A {

   public void show() { }  // 関数内容は省略

}

public class Cmain {

   public static void func(
A obj) {
      obj.show();
   }

   public static void main(String[] args) {
      A objA = new A();
      B objB = new B();

      func(objA);
      func(objB);
   }
}


このプログラムは、

これまで説明してきた事を理解していれば、何も疑問に思う事はないでしょう。

 ただ、この例のように基本クラス型変数を関数の仮引数にするメリットとしては、

基本クラス型の参照値もその派生クラス型の参照値も受け取れる事です。

どちらも受け取れると言う事は、その関数内で

基本クラスと派生クラスに関する同じような処理を行う事ができる。

等の利点があります。

どういう使い方をするかはプログラマ次第ですが、

プログラムの幅が広がる事は確かです。



関数の仮引数にするメリットを活用した例

ファイル名:Cmain.java
class A {

   public void show() { }  // 関数内容は省略

}

class B extends A {

   public void show() { }  // 関数内容は省略

}

public class Cmain {

   public static void func(
A obj) {
      if (
obj instanceof B) {
         ((B)obj).show();  // 派生クラスのshow()呼び出し
      } else {
         obj.show();     // 基本クラスのshow()呼び出し
      }
   }

   public static void main(String[] args) {
      A objA = new A();
      B objB = new B();

      func(objA);
      func(objB);
   }
}


このプログラムでは、instanceof演算子を活用しています。

obj instanceof Bと言う記述は、objにクラスBを含んでいるかどうか

判定します。含んでいるならtrue、含まないならfalseを返します。

func(objA);の呼び出しがあった場合、objAはクラスAの参照なので、

obj instanceof Bに対しては、クラスBを含まないので、falseが返され、

obj.show();が実行されます。

これは、基本クラスのメンバを呼び出す処理です。

一方、func(objB);の呼び出しがあった場合、objBは、クラスBの参照なので、

obj instanceof Bに対しては、クラスBを含むので、trueが返され、

((B)obj).show();が実行されます。

これは、派生クラスのメンバを呼び出す処理です。

以上の事から、

関数内では、引数によって、

基本・派生クラスのメンバ関数の呼び分けを行っている事が分かります。

このプログラム例は、一例です。工夫次第では、まだまだ活用可能でしょう。



スポンサードリンク







HOME

言語解説編
プログラミング用語・知識

C言語

C++言語

Java

C#

Visual Basic.NET

アプリケーション編
C言語による
コンソールプログラミング




Copyright (C) プログラミングランド All Rights Reserved