━目次━
まず最初に言っておきますが、Javaに参照渡しは存在しません。
これ聞いたとき私も「えっ!」ってなりました。
いやいや、参照渡し使ってましたよ自分。
え?それは本当の参照渡しじゃなくて参照の値渡し? ん??
それから色々調べて「おもしれー!!」ってなったのでまとめます。
このページを読み終わる頃にはあなたも「おもしれー!!」ってなっているはずです。
まずは値(プリミティブ)型と参照型について
そもそも値型と参照型を知らない人は。それ以外の人は読み飛ばして結構です。
値(プリミティブ)型とは
値型は以下の8つです。
boolean | 1bit |
---|---|
byte | 8bit |
char | 16bit |
short | 16bit |
int | 32bit |
float | 32bit |
long | 64bit |
double | 64bit |
値型のイメージはあなたがフタの空いた段ボールを持っていて、その中に値を入れているイメージです。
実際にそこに値があります。
参照型とは
参照型はそれ以外の変数たちで、よく使うとこだと
String
配列
List系
DtoやFormなどのクラス etc…
などですね。
初学者は『参照型』と聞くともうそれだけで蕁麻疹が出る程に脳が拒否する人もいますね。
分かるわ~わしもそうじゃ
でも実はそんなに難しくなくて、イメージとしてはロッカーの鍵を持っているイメージです。
実際に値が格納されているのはロッカーの中で、あなたはそのロッカーの番号が書かれた鍵を持っているだけなんです。
まずはざっくりとこのイメージを持っておいてください。
値渡しと参照渡しの違いについて(Javaとか関係なく)
ではJavaのことは1回忘れて俗に言う『値渡し』と『参照渡し』とは何ぞやということですが、
『渡し』と言うくらいなのでファンクションなどに変数(値型or参照型)を渡すときの話です。
まずはソースを見る前に↓のイメージをしてください。
(経験上イメージする前にソースを見ると思考が止まるので…)
【値渡しの場合】
あなたは段ボールを持ったまま中の値を確認してファンクション君に伝えます。
※あくまで伝えるだけです。段ボール大好き人間のあなたは段ボールを手放しません。
【参照渡しの場合】
あなたはロッカーの鍵をファンクション君に渡しちゃいます。
※ファンクション君を相当信用しているんでしょう。ロッカーの中身は好き放題いじられます。
じゃあこのイメージでソースを見てみよう。
【値渡しの動作】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//値型の変数を定義(段ボールの中身は1) int i = 1; //値渡しでファンクションを呼び出す(「段ボールの中は1だよー!」) addNumber(i); //変数の中を表示 System.out.println("俺の値は" + i + "じゃ~!"); //俺の値は1じゃ~! /** * ファンクション君 */ private void addNumber(int i) { //引数に1を足す i = i + 1; //変数の中を表示 System.out.println("俺の値は" + i + "じゃ~!"); //俺の値は2じゃ~! } |
ファンクションを呼び出す際に、段ボールの中身を伝えているだけなので、iの値がファンクションを呼び出した後も変わってないことが分かります。
段ボールフェチですからね。
【参照渡しの動作】
※後述しますがJavaには正確な参照渡しは存在しません!しかし↓のソースなら参照渡しっぽく動くのでまずは雰囲気を掴むために便宜上「参照渡し」として説明しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//参照型の変数を定義(ロッカーの中に"テスト用紙"を入れて鍵を保有) List<String> list = new ArrayList<String>(); list.add("テスト用紙"); //参照渡しでファンクションを呼び出す(ファンクション君に鍵を渡す「任せたゼ」) editList(list); //変数の中を表示 System.out.println("ロッカーの中は" + list + "じゃ~!"); //ロッカーの中は[テスト用紙,カンペ]じゃ~! /** * ファンクション君 */ private void editList(List<String> list) { //ロッカーの中にカンペを追加 list.add("カンペ"); //変数の中を表示 System.out.println("ロッカーの中は" + list + "じゃ~!"); //ロッカーの中は[テスト用紙,カンペ]じゃ~! } |
鍵そのものをファンクションに渡しているので、ロッカーの中は自由にいじられ中身が変わります。
ロッカーの中を確認(println)したら優しいファンクション君がカンペを入れてくれてました。これでテストは無敵だ。
これが一般的な値渡しと参照渡しの動作の違いです。
なるほど~よし!大体Javaのこと分かったけぇもう帰るわ!ありがと!
まてまて(笑)ここからがJava特有の面白いところなんだから
あ?そうなん?(笑)
Javaは参照渡しではなく参照の値渡し
上記では便宜上Javaのソースで『参照渡し』として、『ロッカーの鍵を渡す』と説明しましたが、これは嘘です。
※多くの人が踏む勘違いのステップを踏んでもらうことで違いを分かり易くしようとした意図です。すいません。
※多くのJavaエンジニアが参照渡しだと思い込んで使っていることは事実です…
実際にはJavaでは参照型の変数を渡す場合、『参照の値渡し』が行われています。
参照の値渡しとは
『参照渡し』を『ロッカーの鍵を渡す』ことだと例えるなら
『参照の値渡し』は『ロッカーの鍵に書かれているロッカー番号を伝えること』と言えます。
では先程のソースを正しく説明し直しましょう
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//参照型の変数を定義(ロッカーの中に"テスト用紙"を入れて鍵を保有) List<String> list = new ArrayList<String>(); list.add("テスト用紙"); //参照の値渡しでファンクションを呼び出す(ファンクション君に鍵に書いている番号を伝える「鍵は空けといたゼ」) editList(list); //変数の中を表示 System.out.println("ロッカーの中は" + list + "じゃ~!"); //ロッカーの中は[テスト用紙,カンペ]じゃ~! /** * ファンクション君 */ private void editList(List<String> list) { //ロッカーの中にカンペを追加 list.add("カンペ"); //変数の中を表示 System.out.println("ロッカーの中は" + list + "じゃ~!"); //ロッカーの中は[テスト用紙,カンペ]じゃ~! } |
ん~結局結果は一緒じゃろ?
このソースではそうだね。じゃあ次は参照渡しと参照の値渡しの違いが分かり易いコードを見せてあげよう
参照渡しと参照の値渡しで違いが起きる例
以下のソースを見て下さい
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//参照型の変数を定義 List<String> list = new ArrayList<String>(); list.add("テスト用紙"); //参照の値渡しでファンクションを呼び出す editList(list); //変数の中を表示 System.out.println("ロッカーの中は" + list + "じゃ~!"); //ロッカーの中は[テスト用紙]じゃ~! /** * ファンクション君 */ private void editList(List<String> list) { //再度newする list = new ArrayList<String>(); //ロッカーの中にテスト用紙とカンペを追加 list.add("テスト用紙part2"); list.add("カンペ"); //変数の中を表示 System.out.println("ロッカーの中は" + list + "じゃ~!"); //ロッカーの中は[テスト用紙part2,カンペ]じゃ~! } |
おお!?ロッカーの中身が変わってないぞ??
ロッカーの鍵の現物が無いとロッカーを初期化しようとしても駅員さんが許可してくれない、とイメージすると良いよ。
えー鍵がないとダメなのか…
でもロッカー番号教えてもらっただけだしな…
でもnewしろってうるさいからな…
「そうだ!新しいロッカー申請すれば良いじゃん!」
と、Javaの参照の値渡しの場合こんなことが起こっているのです。
因みに本当の参照渡しの場合(C#で明示的にrefを付ける等)は、鍵があるので同じロッカーを初期化して使いまわすのでどちらの出力結果も
//ロッカーの中は[テスト用紙part2,カンペ]じゃ~!
と表示されます。ロッカーを無駄遣いしなくて良いですね~。
さいごに
私はこの話を知ったとき「おもしれー!!」と思ったのでなるべく初学者の人にも分かり易い形で説明してみました。
因みに、ここでのロッカーやロッカーの鍵番とか言ってるのがメモリやポインタだったりするわけですね。
あとここらへんの変数の書き換えの話に関しては、immutable(イミュータブル)やmutable(ミュータブル)についても知っておかないといけないので興味のある人は調べてみてください。
気が向いたら記事にまとめようと思いますが…
私もまだまだぺーぺーなので何か間違いがあればご指摘頂けると幸いです。
それでは!また!!
人気の記事だけ集めたので是非覗いていってください^^
厳選!目的別にオススメ記事を紹介-あなたの欲しい情報がここに-
最後のほうの「そうだ!新しいロッカー申請すれば良いじゃん!」とは結局、具体的に言うとどうすることなんでしょうか?
メモリに新たな領域を確保することですね!
メモリあたりについてはこちらの記事で解説しています。
https://freelance-jak.com/technology/java/1175/