【Java】関数の仮引数自体の参照を関数内で変えた場合について

どうも。
JavaSilverの勉強をしていると新たな発見もあり
すごく楽しいです。

今回はJavaSilverの勉強の中で詰まったところがありそれの内容が解決したので
それのナレッジで書こうと思います。
参照渡しの変数自体を変更した場合の処理についてです。

前提

・対象バージョン
 Java SE17

問題のロジック

以下のロジックでコンソールに
false
false
と出ると思いました。

public class Main {

	
	public static void main(String[] args) {
		Main main1 = new Main();
		Main main2 = new Main();
		System.out.println(main1 == main2);
		main2 = changeOp(main1,main2);
		System.out.println(main1 == main2);
	}
	
	static Main changeOp(Main ob1, Main ob2) {
		Main ob3 = ob1;
		ob1 = ob2;
		return ob3;
	}
	
}

予測

changeOp関数内で参照型変数のmain1にmain2のオブジェクトを入れ替えており
呼び出し元で元のmain1のオブジェクトにmain2のオブジェクトに代入しておりました。
そのためただ入れ替えているだけだからとfalseになるかなと思いました。
(参照変数の「==」は参照アドレスの比較になるため)

結果

予測は外れ
false
trueとなりました

理由

どうやらメソッドの「参照渡し」には以下のカラクリがあるっぽいです。

・参照渡しで渡されるのは参照変数のアドレス(ポインタ)ではある
・仮引数に入っているのはあくまでも参照変数のアドレス自体がコピーされている
・コピーとは参照変数のアドレスが値渡しとして渡されて仮引数の変数に格納されている(というイメージ)

ちょっとイメージつきづらいので画像付きでまとめてみました。
※画像中に出てくる「11」と「12」はあくまでもポインタのアドレスです。

changeOp(main1,main2);が呼ばれる直前の状態

static Main changeOp(Main ob1, Main ob2)処理に入った瞬間の状態
アドレス自体が仮引数の箱に入る

static Main changeOp(Main ob1, Main ob2)で
Main ob3 = ob1;の宣言と代入をした瞬間
ob1のアドレス情報がob3に格納される

static Main changeOp(Main ob1, Main ob2)の中で
ob1 = ob2;を代入した瞬間
ob2のアドレス情報がob1に格納される。

return ob3;でob3のアドレスを返却して
呼び出し元にてmain2で受け取る↓
main2 = changeOp(main1,main2);
ここで呼び出し元のメソッドにてmain2に返却したob3のアドレスが格納される。

結果アドレスが同じなので同じ箇所を指している形になるので
最後の
main1 == main2
ではtrueになるという形でした。

参考にさせていただいたサイト

こちらの調査をしている中で以下のサイトにめぐりあいとてもしっくりきて納得いたしましたのでご紹介いたします!
以下ページの「参照(値)の値渡し(pass by referenceValue)」をご参照ください。

まとめ

オブジェクトの参照渡しについて
参照先で保持している各変数の中身は呼び出し側のメソッド内で変更したら
呼び出し元にも影響があるけど
仮引数自体の参照変数を入れ替えたらこうなるんだね。。。というので
とても納得しました!やはり参照変数の理解は大事!

今回は以上です!!

スポンサーリンク