#初めに
メソッドに値を渡してメソッド内で値を加工する
という処理を行う際ポインタやC++ のような参照渡しがないJavaではどのようにすればよいかわからなかったので、調べたことや調べる中でこんがらがったこと(主に参照渡し・参照の値渡し)などについてまとめておきます。
なお、記事を書いているが当人はJavaに触れてまだ3か月程度なので間違い等あれば指摘していただければ幸いです。
#値渡し
例えばメソッドに引数とした値をメソッド内で2倍にするコードを下記のように書いてみます。(このコードは意図したとおりには動きません)
public class Main{
/*メソッド内で値を2倍にする*/
public static void twice(int n){
n *= 2;
}
/*メイン関数*/
public static void main(String[] args){
int num = 2;
twice(num);
System.out.println(num); //2
}
}
結果としてはnumの値はメソッドの中で書き換えられず、そのまま初めに宣言された2が出力されます。
これはメソッドに変数numをそのまま渡しているように見えますが、あくまでメソッドに渡されているのはnumの中の値のみだからです。
これを値渡しといいます。
#Javaで値を書き換えるには
値を書き換えようと思ってもポインタや参照渡しがない(今はこのように記述します)Javaでどのように値を書き換えればよいのでしょうか。
方法としては配列やクラス型の変数を使えばよいのです。
######配列を使う例
public class Main {
/*配列を渡すよう変更*/
public static void twice(int[] n){
n[0] *= 2;
}
/*メイン関数*/
public static void main(String[] args){
int num[] = new int[1];
num[0] = 2;
twice(num);
System.out.println(num[0]); //4
}
}
######値を保存する用のクラスを利用する例
public class Data {
int num; //データ保存用のフィールド
}
public class Main {
/*データ保存用クラスのインスタンスを渡すよう変更*/
public static void twice(Data ins){
ins.num *= 2;
}
/*メイン関数*/
public static void main(String[] args){
Data data = new Data();
data.num = 2;
twice(data);
System.out.println(data.num); //4
}
}
それでは、なぜ配列やインスタンスならばメソッド内での値を書き換えることができるのでしょうか。
ここからはJavaのメソッドの引数についてみていきます(値渡し・参照渡し・参照の値渡しで混乱した人向けになります)。
#Javaのメソッドの引数
誤解を招く表現かもしれませんが自分はここで躓いたので初めに言います。
Javaには値渡ししか存在しません。
参照渡し・参照の値渡しについては一旦忘れてください。
######Javaにおいてメソッドに渡されるもの
Javaには値渡ししか存在しませんといいましたが、変数の種類によって渡される値の種類が二つあります。
まずはその変数の種類についてJavaには大きく二つに分けて
・基本型
・参照型
の変数があります。
そしてそれぞれメソッドの引数に設定された時渡すものが違ってきます。
######基本型の時
まず基本型とは、
データ型 | 数値の分類 |
---|---|
byte | 整数 |
short | 整数 |
int | 整数 |
long | 整数 |
floot | 浮動小数 |
double | 浮動小数 |
boolean | 真偽 |
char | 文字 |
のことを指します。 | |
これらをメソッドの引数として渡す場合Javaでは値渡しのみが行われます。 | |
つまり、変数の中身の値だけが渡されるためメソッド内で引数の値を変更しても渡した引数の値は変更されないということです。 |
######参照型の時
次に参照型には
・クラス型
・配列型
などがあります。
これらはメソッドの引数になったときにその参照値を渡します。
さて、ここでの参照値とは何でしょうか。
#参照値とは
参照値とはイメージ的に言うと
その変数の存在している場所
のことになります。C言語でいうとポインタのようなもののイメージです。
######なぜメソッド内で値が書き換えられるのか
Javaはメソッド内で引数を変更することができません(値渡ししか存在しないので当然です)。
しかし、参照値を渡すことでその参照値の場所を介して値を書き換えることができるようになります。
######参照渡しと何が違うのか
ここで、参照渡しと何が違うのかということですがあくまでJavaでは
参照値というその変数の存在している場所の値をコピーして渡す値渡し(当然参照値について書き換えることはできない)
という動作を行っています。
参照渡しは
変数の存在している場所をそのまま渡す
という動作をおこなっています。
結果的に動作としては似たようなものになりますが、Javaは参照渡しを行っているわけではなく参照値を値渡ししています。
######参照値を値渡ししているということが分かる例
先ほどのデータ保存用クラスを利用したメソッド内での値の書き換えを行っていたコードについて以下のように書き換えます。
public class Data {
int num; //データ保存用のフィールド
}
public class Main {
/*クラスのインスタンスを渡す*/
public static void twice(Data n){
/*新しいインスタンスをメソッド内で作成*/
Data inMethodInstance = new Data();
/*データを与える*/
inMethodInstance.num = n.num * 2; //4
/*参照値の書き換え*/
n = inMethodInstance;
}
/*メイン関数*/
public static void main(String[] args){
Data data = new Data();
data.num = 2;
twice(data);
System.out.println(data.num); //2
}
}
このようなコードに変更して実行してみると2が出力されているのが分かります。
参照渡しが行われている場合、メソッド内の最後で行われている
n = inMethodInstance
の部分でnが書き換えられて4が出力されるはずですよね。
ちなみにこのJavaで書いたコードをC++で参照渡しを行うようにコードを書くと下記のようになります。
#ifndef DATA_H_
#define DATA_H_
/*データ保存用クラス*/
class data
{
public:
int num;
};
#endif
#include <iostream>
#include "data.h"
void twice(data &d) {
/*メソッド内でインスタンス生成*/
data inMethodInstance;
/*メソッド内のインスタンスに値を与える*/
inMethodInstance.num = d.num * 2;
/*引数を書き換える*/
d = inMethodInstance;
}
int main(void)
{
data ins;
ins.num = 2;
std::cout << ins.num << std::endl; //2
twice(ins);
std::cout << ins.num << std::endl; //4
}
このように参照渡しを行うことができるC++ならば引数の値を書き換えることができます。
#Javaに参照渡しは存在しない
何度目かになりますがJavaに参照渡しは存在しません。
値渡しのみが存在しています。
ただし、参照値を介することで値を変更することができます。
この「参照値を介することで値を変更できる」という部分で参照渡しが存在する、と勘違いをしてしまうのだと思いますが、あくまでJavaには値渡ししか存在していません。
#終わりに
今回はJavaのメソッド内で値を書き換える方法について調べたものをまとめてみました。
初めて記事を書いた、ということもあり読みにくい部分もあるかと思いますが多めに見てください…