【Java入門(3)オブジェクト指向:活用編 #6】参照型の型変換

 

みのる
こんにちは。笑顔で感謝!✨ みのるコーチです。
今回も、ご覧いただき、ありがとうございます。

今回は…

Java入門3 オブジェクト指向:活用編 #6
参照型の型変換

というテーマでお送りします。

彩香
基本データ型については 超入門で勉強しました。

そして今度は、参照型の型変換ですね。
今回も、頑張ります。

剛留
オブジェクト指向のプログラミングが分かってきました。
今回も、楽しみです。よろしくお願いします。

【1】 参照型の型変換

みのる
超入門で、基本データ型の型変換について
学習しましたね。覚えていますか?

それに引き続いて、今度は…

参照型の型変換

です。

まず、イメージから見ていきましょう。

ある クラス ClassA があります。

ClassA の中で定義されているメソッドは
methodX() と methodY() です。

ClassA を継承したクラスClassB があります。

ClassAがスーパークラス
ClassBがサブクラスですね。

ClassBの中で追加で定義されているメソッドは
methodZ()です。

この ClassB をインスタンス化して 変数に代入します。

ClassB b = new ClassB();

右辺の意味は、インスタンス化(実体として領域確保)
された型はClassBという事です。

左辺の意味は、ClassBという参照型で
変数 b が宣言されて…

右辺でインスタンス化された
先頭のアドレスが格納されるという事です。

右辺のインスタンスの型はClassB。

左辺の参照の型もClassBです。

この先頭アドレスが例えば100番地としましょう。

この b には インスタンス化された
先頭の番地が格納されます。

bの値は100番地です。

そして、ClassBを指し示すbがアクセスできる範囲は
ClassB 全体ということになります。

methodX();
methodY();
methodZ();

…に、アクセスできます。

みのる
継承のところで学びましたね。

そして、参照型の型変換です。

ClassA a = b;

このように記述すると

変数aに
変数bの値を代入することとなります。

値は変わらず100番地ですが…

参照の型(変数の型)が変わっています。

参照の型はスーパークラスのClassAです。

このように、サブクラスの参照値を
スーパークラス型の変数に代入する事ができます。

これを

アップキャスト

といいます。

変数aの参照の型はClassAです。

こうなると a でアクセスできる範囲は
ClassAの範囲となります。

methodX() と methodY()

にはアクセスできますが…

methodZ()

にはアクセスできない事となります。

そして、参照型の型変換をもうひとつ。

ClassB b2 = (ClassB) a;

このように記述すると

変数b2に
変数aの値を代入することとなります。

値は変わらず100番地ですが
参照の型が変わっています。

参照の型はサブクラスのClassBです。

このように、スーパークラスの参照値(アップキャスト後)を
サブクラス型の変数に代入する事ができます。

これを

ダウンキャスト

といいます。

サブクラスの中には、スーパークラスの部分を
全て含んでいるので…

アップキャストの場合には
自動的に(参照の)型変換が 行われます。

これに対して、ダウンキャストは
参照する範囲を 広げる形になりますので…

プログラマが明示的に キャスト演算子「()」を用いて
Javaに ダウンキャストして大丈夫な旨を 伝えます。

ここで、変数b2の参照の型はClassBですので。
アクセスできる範囲は、ClassB全体となります。

methodX();
b2.methodY();
b2.methodZ();

の、3つのメソッドにアクセスできる事となりますね。

この部分、このイメージで押さえておきましょう。

ここまでをまとめると、こうなります。

(アップキャスト:自動型変換)

✅ サブクラス型の変数をスーパークラス型の変数に代入できる。
✅ 型変換されるのは変数の型。つまり参照の型。
✅ アクセスできる範囲は、スーパークラス部分に限定される。
 (参照の型により、アクセスできる範囲が決まる。)

(ダウンキャスト:明示的型変換)

✅ インスタンス(実体)サブクラス型の場合
  スーパークラス型の変数をサブクラス型の変数に代入できる。
✅ キャスト演算子「()」を用いる。
✅ アクセスできる範囲は、サブクラス全体となる。
 (参照の型により、アクセスできる範囲が決まる。)

先程のイメージと併せて、押さえておきましょうね。

彩香
アップキャスト、ダウンキャスト…。
意味の違いが、バッチリ分かりました。
剛留
参照のキャストは、アクセスできる範囲が
変わるという事なんですね。
 
ガッテン、承知の助。

【2】 インスタンスの型

前のコーナーでお話した、型変換については
あくまで…

参照の型

についての変換方法です。

この、参照の型によって

アクセスできる範囲が決まる

ということです。
そして…

インスタンス(実体)の型は 変わらない

ということを理解しておいてください。

ClassB から ClassA に型変換されても
範囲外が消されるということはありません。

インスタンス(実体)の型は不変

ということ
押さえておきましょうね。

【3】 instanceof

Javaでは、継承関係にあるオブジェクトを操作する場合…

参照の型(変数の型)と
インスタンスの型(実体の型)が
違っている

場合が多くあります。

例えば、このような場合です。

ClassA a = xxxx;

変数 a についての
参照の型は ClassA ですが…

そのインスタンスの型は
実行した時の状況により、異なる場合があります。

(サブクラスの場合も考えられます。)

このような場合に、この後の処理の中で…

サブクラスの型にダウンキャストして
使いたい時もあります。

この時には、ダウンキャストする時点で…

インスタンスの型が 期待する型かどうか
調べてからキャストする 必要が出てきます。

その、調べる方法が

変数名 instanceof 型名

という構文で調べる方法です。

例えば

a instanceof ClassB

この場合、変数aの指し示す実体が
ClassBか そのサブクラスの時に…

この式自体が true になります。

そうでない場合には false です。

つまり、

ダウンキャストが可能な場合に true

です。

【4】 ダイナミックバインディング

ここまで…

変数の型(参照の型)を変換すると
アクセスできる範囲が決まる

…というお話をしてきましたが…

今度は、メソッド実行時を考えてみましょう。

ここで、1つ問題です。

前のコーナーの例で…

スーパークラス ClassA のメソッドのうち

methodY() について、サブクラス ClassB で
オーバーライドしている場合を考えましょう。

この場合には、サブクラス ClassB で
methodY() をオーバーライド(再定義)しています。

利用する側で、ClassB をインスタンス化して
ClassB型の 変数 b に代入します。

ClassB b = new ClassB();

そして、ClassA にアップキャストします。

ClassA a = b;

前のコーナーまでの話で…

ここで、アクセスできるのは
ClassAで定義されている範囲になりました。

ここからが、問題です…。

利用する側で

a.methodY();

このように、methodY() を呼び出した時に…

実行されるのは?

① スーパークラス ClassA で定義した methodY()

② サブクラス ClassB で再定義した methodY()

どちらでしょう?

彩香
参照の型が ClassA で、アクセスできる範囲が
ClassA の範囲だから…。

最初に ClassA で定義した methodY() と思います。

剛留
僕も、ClassA で最初に定義したほうと思うけど…。

もしかして、引っ掛けで
ClassB で再定義したほうかな…?

みのる
では、早速、正解を…。

これは、

ClassB でオーバーライドした methodY()

が実行される事となります。

この場合、インスタンス(実体)の型はClassB。

そして、参照(変数)の型はClassA ですね。

このような場合…

Javaでは 実行時に、インスタンスの型に従って
実行するメソッドが 決定されます。

ここでは、実体が ClassB のインスタンス ですので
ClassB でオーバーライドした メソッドが 実行対象となります。

これを

ダイナミックバインディング(動的バインディング)

といいます。

大切なところなので、もう一度…

✅ アクセスできる範囲は、参照(変数)の型で決まります。

✅ 実行する対象は、インスタンス(実体)の型で決まります。

✅ 実行時に、動的に決まるので ダイナミックバインディング
  (動的バインディング)といいます。

ここも、このイメージで 捉えておきましょうね。

【まとめ】

【1】 参照型の型変換
✅ サブクラス型の変数は
  スーパークラス型の変数に代入できる。
  (アップキャスト)

✅ スーパークラス型の変数は
  キャスト演算子を使って
  サブクラス型にキャストする。
  (ダウンキャスト)

✅ 参照の型により、アクセスできる
  メンバの範囲が決まる。

【2】 インスタンスの型
✅ 型変換できるのは、参照の型。

✅ インスタンス(実体)の型は不変。

【3】 instanceof
✅ インスタンスが期待する型かどうか
  調べる方法。

   変数名 instanceof 型名

【4】 ダイナミックバインディング
✅ インスタンスに アクセスできる範囲は
  参照(変数)の型で決まる。

✅ 実行する対象(メソッド)は
  インスタンス(実体)の型で決まる。

✅ 実行時に、動的に対象がが決まるので
  ダイナミックバインディング
 (動的バインディング)という。