【Java】オーバーライドとオーバロードが同時に存在した場合のポリモーフィズムについて

どうもかなり久しぶりの更新です。
実は子供が産まれ、結構バタバタしており更新がかなり滞っておりました。
子育ては大変ですが夫婦共々頑張って育児をしております!
JavaSilverの勉強は相変わらず続けておりまだ受験には至ってないです。
ですがそんな中ポリモーフィズムで少しややこしかったことがあったので理解を兼ねて仕組みをアウトプットしようかと思います。

前提

・対象バージョン
 Java SE17

・前提知識として以下のことをなんとなくでも理解している方
 オーバーロード
 オーバーライド
 ポリモーフィズム

対象のコード

Super.java

import java.util.Collection;

public class Super {

	public void sample(Collection args) {
		System.out.println("A");
	}
}

Sub.java

public class Sub extends Super{

	public void sample(Collection args) {
		System.out.println("B");
	}
	
	public void sample(List args) {
		System.out.println("C");
	}
}

Main.java

import java.util.ArrayList;
import java.util.List;

public class Main {
	public static void main(String... args) {
		Super super1 = new Super();
		Super super2 = new Sub();
		Sub sub1 = new Sub();
		
		List list = new ArrayList<>();
		
		super1.sample(list);
		super2.sample(list);
		sub1.sample(list);
		
	}
}

気になった箇所

ポリモーフィズムを使用している

Super super2 = new Sub();
//〜〜省略
super2.sample(list);

の部分が気になりました。

super2はSuperクラス型ですがインスタンスとしてSubを持っている形になるため
引数の型がList型に厳密にマッチしていると思って
オーバーロードしている

	public void sample(List args) {
		System.out.println("C");
	}

が呼ばれた結果、コンソールの2番目にはCが表示されると思いました。

実行結果


結果は異なってました。。。

理由

ポリモーフィズムを利用した場合
コンパイル時、及び実行時に以下のような動きになる模様です。

ポリモーフィズムで実装した際のコンパイル時

コンパイル時に以下をチェック(チェック順については順不同。大まかにざっくりこんなイメージかと思ってます)
・変数宣言した際に、呼び出したメソッドが参照変数の型にいるかどうか
・参照変数の型に呼び出したメソッドがいる場合は、アクセス制御を持ってアクセス可能かどうか

ポリモーフィズムで実装した際の実行時

実行時にJVMが以下の動きをする
・参照変数の型に呼び出したいメソッドがいて尚且つインスタンス側のクラスでオーバーライドされている場合
 オーバーライドされている方のメソッドを呼ぶ。
・同名のメソッド名でサブクラス側でオーバーロードされている場合は、オーバーロードされている方は呼ばれない
・呼ばれない理由は、スーパークラス側にそのオーバーロードされている方のメソッドに匹敵するものがいないため。

今回のコード的に説明すると

ポリモーフィズムで実装した際のコンパイル時

Super super2 = new Sub();
super2.sample(list);

コンパイル時に以下をチェック
・Superクラスにsuper2.sample(List型)で呼び出せるメソッドがいるかどうかをチェック
 ->今回の場合
  「Listインターフェース」
  「Collectionインターフェース」のサブインターフェース
   なので互換性ありとして「メソッドあり」と認識される

・super2.sample(List型)呼び出しの際、Superクラスの「Sapmleメソッド」のアクセス制御を見てアクセス可能かどうか
 ->今回の場合
  Superクラスの「Sapmleメソッド」のアクセス制御がpublicのためどこからでもアクセス可能

ポリモーフィズムで実装した際の実行時

実行時にJVMが以下の動きをする
・Superクラスにsampleメソッドはいるけど、
 インスタンスとしてはSubクラスを持っているので
 Subクラスにオーバーライドされているsample(Collection args)メソッド側が呼ばれる。

・Superクラスにsample(List args)がいないため、Subクラスのインスタンスを持っていようが、
 Superクラス側を基準とするため、sample(List args)は呼ばれない。

ポイント

まとめると以下のイメージでいいかと思います。
ポリモーフィズムでインスタンス化をした場合

コンパイル時
・変数宣言した時の型の方を基準にコンパイルする

実行時
・変数宣言した時の型にそのメソッドがいれば、インスタンス側のクラスでオーバーライドされているか確認する
・オーバーライドされていれば、インスタンス側のクラスのメソッドを使う
・オーバライドされていなければ型の方のクラスのメソッドを使う
・インスタンス側でクラスでオーバーロードされていても、型の方でメソッドがいなければインスタンス側のオーバーロードのメソッドは無視される。

こんなイメージかなぁと。。。

まとめ

今回はJavaSilverの勉強中に少し混乱してしまったため
アウトプットしつつ理解しようと思った次第になります。

資格の合格目指して勉強頑張ります!!

今回は以上!!!

スポンサーリンク