MistiRoom

それでも、何かを語るのだ

Javaのややこしい「(疑似)参照渡し」を可能な限り分かりやすく説明する

 ども、Mistirです。

普段このブログでエンジニアリングのことは書いていないけど、やっと悩みが解決できたから覚書程度に。
※普段読んでくださってる方、ごめんなさい。
この記事は完全にプログラマ・SE向けの記事です。

さて。
そもそも前提だけど……
Javaに「参照渡し」は存在しない。
これはよく言われてるけど、案外物凄く難しい。

この記事は比較的分かり易いが……

qiita.com

 

実は、僕はこの記事で言われている

参照型は参照渡しだけど、String 型だけは値渡し説

で「仕方なく」Javaの参照渡しについて理解していた。
この考え方が間違ってるのは「なんとなく」理解していたのだけど……

参照渡し説を取る人たちの中でも、「String 型は参照型だけど呼び出し元で値を変えられないから、String 型だけは値渡し」という派閥があるようなのです。
しかし、これは引数の渡し方はもう全く関係のない話になってきます。単に String が immutable であるというだけの話です。 

 ……この説明。
初学者の僕には少し難しかった……
immutableの概念がなかなか理解できなかった。

……ということで。
ずっと理解できなかったんだけど、ようやくおぼろげに全体像が見えてきた。
ってことでなるべく分かりやすく書いてみようと思う。

【対象読者】

  • 基本型とそれ以外の違いは何となく分かるぜ!って人
  • Stringの挙動が基本型っぽいのもなんとなく分かるぜ!って人
  • っていうかJavaほとんど分からんけどとりあえず読んどけって人
  • 久しぶりにドラクエがしたい人

 

 

よくある例題

まず、分かり易い?例から。

基本型のいわゆる「値渡し」を語る上で外せないこのコード。

sample01


が、実はこの時点で既に難しい。
「簡単やんけ!!」とか言わずに、少し付き合ってほしい。


変数であるmistir君に3行目で1が代入される。
これはどういうことかっていうと、「1って値を覚えろ」と言われているワケだ。

そう。
「1という値は、mistir君の頭の中に」ある。

で。
addメソッドの中で待ち構えているエスタークに、「値渡し」するのだ。
変数名は分かりやすく?知るか!

どうやって値を渡すか。

「白いカードに頭の中の値を書いて、渡す」
のです。

大事だからもう一度言います。
「白いカードに頭の中の値を書いて、渡す」
のです。

この場合、カードには「1」という値が書かれている。
エスターク君はそれを受け取る。
で、こう呟くわけだ。
「1か。覚えたよ!!!」
エスタークはそんなこと言わない

そして、計算して、答えである「2」を……
「覚える」













そして、何も起こらない。
虚しい。





addメソッドは終了。
最後に出力されるのは、mistir君の頭の中にある値……
つまり、1が出力される。

少しでも経験があると「当然」「常識」のような話だけど、この「値渡し」のイメージを「値そのもの」と解釈してると……
つまり、「たった一枚だけ存在する『1と書かれたカード』をやり取りしている」と解釈すると、初学者は躓きやすい気がする。
というか僕はそこで躓いた。
それはどっちかというと参照渡しの動きになってしまうからだ。

さて、次は参照渡しの説明に使われるこのコード。

sample02
詳しい解説は後にしよう。
ふざけすぎて逆に分かりにくい気がするが、6行目には文字列「死」が出力される。
っていうかエスタークってザキ使ったっけ……

Listは参照型だから参照渡しになる、というか基本型以外は全部参照渡し。
教科書的に言えば、そうだ。

……だけど。
なんだけど。

このコードは……



 

sample03








そう、Stiring型。
お前は一体何なんだ。

String型はもちろんオブジェクトであって、基本型じゃない。
なのに、この挙動はなんなんだ。

「String型はimmutableだからオブジェクトそのものを触れないだけっすよ」。

……難しいわ!!!!

ってことで、なるべく噛み砕きます。

 

String、なんなんだお前はよ……

さあ。
さっきのListを使ったコードに戻ろう。

 

sample02


さて、このコードを一つずつ解説する。
そうすると「Stringって何なんじゃ???」というのがわかってくるはずだ。

まず、mistir君にインスタンスが代入されている3行目。
ここでは2つのことが行われている。

一つ目は、当たり前だけどインスタンスの生成。
もう一つは……
インスタンスの場所という「情報」の、変数mistir君への格納だ。

どういうことか。

まず、PCのメモリの中に旅の宿屋1号室から999号室があると考えてほしい。
急に何の話だ?
……と思われるかもしれないが、とにかくそう考えてほしい。

さて。
インスタンスが生成される、つまりArrayListの実態」が生まれる際、生まれたインスタンスは、メモリのどこかに格納される。

今回は「20号室」に格納されたとのこと。

そして、代入のタイミングで変数mistir君に情報が伝えられるわけです。
「20号室にListが生まれたよ」と。

mistir君は「ふむふむ、20号室なんだな」と「覚える」。

そして、5行目のメソッドzakiでエスターク君に……



「真っ白なカードに『20号室』と書いて、エスターク君に渡す」。





そして、エスターク君は「20号室」という番号を覚える。

そして11行目。
zakiメソッドの中で。

エスターク君は、記憶を頼りに。




「20号室のインスタンスを『操作』する」。



さあ、mainメソッドに戻ろう。
6行目の標準出力で、出力されるのは……



mistir君の頭の中にある部屋番号「20号室」に存在するListの要素だ。



これが、一部の人が「参照の値渡し」と呼んでいる、ある意味奇妙なJavaの挙動だ。
「参照の値」とは、この場合「20号室」という部屋番号の情報です。


さぁ、Stringがなぜ「値渡し」のような挙動をするのか。
その話に戻ろう。

実は、このコードで解説できる。

 

 

sample04


普通に「参照型」の挙動を考えるなら、「死」が出力されそうなものだ。
が、ここで出力されるのは「叡智」だ。

途中までは先程の解説と同じ。

「20号室」にArrayListの本体が生まれ、格納される。

その後、「20号室」と書かれたカードがmistir君からエスタッ君に手渡される。

さあ。
11行目、インスタンスが生成される。




どこに?





新しい部屋に、だ。

「21号室」としよう。

そして、エスタッ君はmistir君から渡された「20号室」のことなんて忘れて、21号室にあるArrayListに……

†死を与える†

わけだ。

そして、mistir君の頭の中にあるのは「20号室」という部屋番号のまま。
だから最終的に「叡智」という文字列が出力される。


もう、お分かりだろう。
このコード。

sample03

String mistir = "叡智";

 この記述でさりげなく……
「叡智」という「実態」が、メモリ上に生まれている。
「30号室」としよう。
よく考えると当然だ。「参照型」なんだから。
オブジェクトと同じ挙動をするのだから。


そしてzakiメソッドの中で何が起こるか。

estark = "";

先程言った「String型はオブジェクトそのものを直接触れられない(immutable)」。
これはどういうことか。

30号室で眠っている「死」を直接動かせないということだ。
なんか詩的

じゃあ何が起こるか?
上の記述で、何が起こっているのか?






"31号室"に、「死」が生まれるのだ。




それが「immutable」、つまり、「都度暗示的にインスタンスが生成される」ということだ。



必然的に、最後に出力されるのはmistir君の頭の中に残る「30号室」という部屋番号の、中身。
つまり「叡智」になるわけだ。

終わりに

ドラクエやりたい

アルティメット ヒッツ ドラゴンクエストIV 導かれし者たち

アルティメット ヒッツ ドラゴンクエストIV 導かれし者たち

 

 

 

 

 

 

お読み頂き、ありがとうございました。
ではまた次の記事で。

【余談】
普段全く技術ネタ書いてないので、普段の読者さんには申し訳ないですが、好き勝手に書く路線でこのまま突っ切ります。今後ともよろしくお願いしますね。