Java でメソッドを呼び出すときの引数についてのお話です。
今回は参照型編。プリミティブ型(基本型)の記事はこちら。
よく「Java ではすべて値渡し」って説明を初心者にしている人を見ます。決して間違いではないのですが、ちょっと不親切な気がします。
丁寧に「参照の値渡し」と教えてあげましょう。
プリミティブ型と参照型
あくまでイメージですが、プリミティブ型の変数は値そのものを保持しています。「int i = 3;」となっていた場合、変数 i には 3 が格納されています。
一方、参照型はオブジェクトそのものではなく、オブジェクトが存在する場所(アドレス)を保持しています。
「String s = "いろはにほへと";」となっていた場合、メモリのどこかに "いろはにほへと" という文字列が作成されます。参照型の s には、そのアドレスが保持されます。
たとえば、メモリ上のアドレス「0x20A5」に "いろはにほへと" が作成された場合、変数 s には「0x20A5」が格納されます。
ちなみに、Java ではその具体的なアドレス(この例では「0x20A5」)を確認することはできません。
サンプルを通じて理解する
参照渡しと勘違いする例
メインクラスとは別に、引数で渡すためのクラスを宣言しておきます。
public class Obj { public int a; }
そしてメインクラス。
public class Argument { public static void main(String[] args) { Obj o = new Obj(); o.a = 2; methodA(o); System.out.println("oが持つ変数aの内容は[" + o.a + "]"); } private static void methodA(Obj o) { o.a = 3; } }
実行結果
oが持つ変数aの内容は[3]
o.a に 2 をセットしてから、methodA を呼び出します。methodA 内で o.a に 3 をセットしなおしています。
したがって、methodA の実行が終了した後、 o.a を出力すると 3 と表示されます。
ただ、これだけだと「なんだ、参照渡しじゃん」と言われます。
参照の値渡しを理解する例
メインクラスをちょっと変えて、これも見せてやります。
public class Argument { public static void main(String[] args) { Obj o = new Obj(); o.a = 2; methodA(o); System.out.println("oが持つ変数aの内容は[" + o.a + "]"); } private static void methodA(Obj o) { o = new Obj(); o.a = 3; } }
実行結果
oが持つ変数aの内容は[2]
または、こう↓。
public class Argument { public static void main(String[] args) { Obj o = new Obj(); o.a = 2; methodA(o); System.out.println("oが持つ変数aの内容は[" + o.a + "]"); } private static void methodA(Obj o) { o = null; } }
実行結果
oが持つ変数aの内容は[2]
methodA で o に null を代入しているのにもかかわらず、呼び出し元に戻って o の変数 a を表示したら、methodA を呼び出す前の値が表示できます。
つまり、参照そのものを渡しているのではなく、参照をコピーしてから渡しているのです。
なので、「参照の値渡し」と理解しておくのが良いと思います。
ちなみに C++ でオブジェクトの値渡しというと、オブジェクトをまるまるコピーしてから渡すので、C++ 経験者に対して「Javaはすべて値渡し」っていうと、思いっきり勘違いされそうな気がします。
2017年4月9日追記:
下の記事で、「参照の値渡し」という言葉がディスられています。
qiita.com
そして「参照の値渡し」
このような Java にかかわる「参照」という用語の使われ方に混乱した人たちや、混乱を治めようとする人たちが、「参照の値渡し」とか「共有渡し(call by sharing)」なる用語を開発しています。
しかし、「参照の値渡し」という人たちの、「参照の値渡し」にという用語における『参照』とはどのようなものを想定しているのかを考えるにつけ、Java において『「参照」といえば「参照値」という「値」』という前提をむしろ理解しにくくしているような気がします。
まあ確かにおっしゃるとおりで、私は「参照の値」を「参照」と省略して、当記事で使っています。
正確に言う場合は「参照値の値渡し」という言葉を使ったほうが平和を保てるかもしれません。
まとめ
Java は全て値渡しです。が、参照型を渡す場合、参照値を値渡しするので、参照渡しと勘違いされやすいので注意しましょう。
いろんな記事を読んでしっかり理解しましょう。参考リンクです。
Javaにおける値渡しと参照渡し
javaの引数 プリミティブ型(基本型)は値渡し - 山崎屋の技術メモ
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: 丸善出版
- 発売日: 2018/10/30
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
- 作者: 高橋麻奈
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2016/08/31
- メディア: 単行本
- この商品を含むブログを見る