HOWTO · Java
Java で値によって渡される配列または参照によって渡される配列
この記事では、配列が値によって渡されるのか、Java で参照によって渡されるのかを確認する方法を紹介します。
このページの内容
Javaでは、すべてのメソッド引数が値渡しされます。引数が配列の場合、仮引数にコピーされる値は配列オブジェクトへの参照です。そのため、呼び出し元と仮引数は同じオブジェクトを参照できますが、それぞれ別の変数です。これが、要素の変更は呼び出し元から見える一方、仮引数に別の配列を代入しても呼び出し元には反映されない理由です。
Javaの引数の渡し方
参照とは、オブジェクトにアクセスするための値であり、オブジェクトそのものではありません。メソッドが受け取るのは、その参照値のコピーです。呼び出しが始まった時点では、呼び出し元の配列変数とメソッドの仮引数は同じ配列オブジェクトを参照しています。Javaでは配列変数そのものを参照渡ししないため、メソッド内で仮引数に代入しても、呼び出し元の変数を置き換えることはできません。
ここで区別すべきなのは、オブジェクトを変更することと、変数を変更することです。配列要素への代入は、共有されているオブジェクトを変更します。一方、仮引数への代入が変更するのは、コピーされた参照を持つ仮引数だけです。どちらも呼び出されたメソッド内で行われますが、結果は異なります。
配列要素の変更が呼び出し元に及ぼす影響
メソッドがvalues[index]に値を書き込むと、どちらの参照からもアクセスできる配列オブジェクトの要素が変わります。メソッドから戻った後、呼び出し元からも変更後の要素を確認できます。参照自体は値渡しされていますが、コピーされた参照が同じオブジェクトを指しているため、要素の変更が見えるのです。
メソッドが配列の仮引数に別の配列を代入するとどうなるか
仮引数に新しい配列を代入すると、その仮引数は別のオブジェクトを参照するようになります。元の配列を参照し続ける呼び出し元の変数は変わりません。再代入する前に元のオブジェクトに加えた変更は呼び出し元から確認できますが、仮引数が新たに参照する配列は呼び出し元には渡りません。呼び出し元の配列変数を置き換えるには、新しい配列を戻り値として返し、呼び出し側でその戻り値を代入します。または、元の配列を変更します。
プリミティブ型の仮引数の動作
プリミティブ型の引数も値渡しされるため、仮引数にはプリミティブ値のコピーが入ります。メソッド内で仮引数に別の数値を代入しても、呼び出し元の変数は変わりません。これは配列要素の変更ではなく、配列の仮引数への再代入に似ています。配列の場合は参照が加わり、コピーされた値を使って共有オブジェクトを変更できますが、新しい参照を代入しても変わるのはローカルな仮引数だけです。
次の自己完結した例を実行してください。
このセクションのコードと表示されるラベルや出力は英語のままです。
public class ArrayArgumentDemo {
static void changeFirst(int[] values) {
values[0] = 9;
}
static void replaceParameter(int[] values) {
values = new int[] {7, 8};
}
static void changeNumber(int number) {
number = 9;
}
public static void main(String[] args) {
int[] numbers = {1, 2};
changeFirst(numbers);
System.out.println(numbers[0]);
replaceParameter(numbers);
System.out.println(numbers[0]);
int count = 1;
changeNumber(count);
System.out.println(count);
}
}
9
9
1
最初の出力は、変更後の要素を示します。2番目の出力からは、メソッドの仮引数に別の配列を代入した後も、呼び出し元の配列が変わっていないことが分かります。最後の出力は、呼び出し元のプリミティブ型変数も変わっていないことを示します。
要点
Javaでは、配列への参照を含め、引数は常に値渡しされます。呼び出し元とメソッドの仮引数が同じ配列オブジェクトを参照しているとき、要素の変更は呼び出し元からも見えます。仮引数への再代入で変わるのはそのローカルな参照だけであり、プリミティブ型の仮引数への代入で変わるのもローカルなコピーだけです。オブジェクトの変更と仮引数への代入を分けて考えれば、動作を予測できます。