Javaの引数は値渡しなのか、参照渡しなのか


正直知ってても知らなくても困ることはないのですが、Javaの引数の渡し方のお話です。

Javaは参照渡しと言われることが多くて、でもなんだかそのあたり腹落ちせずもやもやしてたのがC++の勉強をしていて霧が晴れた感があったので記事にしてみました。

くり返しになりますが、別にJavaに限っていったら知っててても知らなくてもどうでもいいことです。

プリミティブ型の場合

    public static void main(String[] args) {

        int a = 1;
        int b = 2;

        System.out.println("Before swap");
        System.out.println("a = " + a);
        System.out.println("b = " + b);

        swap(a, b);

        System.out.println("After swap");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

    static void swap(int x, int y) {

        System.out.println("In swap");

        int t = x;

        x = y;
        y = t;

        System.out.println("x = " + x);
        System.out.println("y = " + y);
    }
出力
Before swap
a = 1
b = 2
In swap
x = 2
y = 1
After swap
a = 1
b = 2

 swapメソッドの引数xyは呼び出し元のabのコピーであり異なる実体です。したがって、swapメソッドの中でxyの値が入れ替わっても呼び出し元に影響は及びません。

これがすなわち値の値渡しです。

オブジェクトの場合

    public static void main(String[] args) {

        Integer c = new Integer(1);
        Integer d = new Integer(2);

        System.out.println("Before swap");
        System.out.println("c = " + c);
        System.out.println("d = " + d);

        swap(c, d);

        System.out.println("After swap");
        System.out.println("c = " + c);
        System.out.println("d = " + d);
    }

    static void swap(Integer x, Integer y) {

        System.out.println("In swap");

        Integer t = x;

        x = y;
        y = t;

        System.out.println("x = " + x);
        System.out.println("y = " + y);
    }
出力
Before swap
c = 1
d = 2
In swap
x = 2
y = 1
After swap
c = 1
d = 2

結果はプリミティブ型と同じく、swapメソッド内で値が入れ替わっても、呼び出し元の値は入れ替わりません。
swapメソッドにはIntegerクラスのインスタンスに対する参照が引数として渡されます。
ただし、引数に渡される参照は、プリミティブ型の場合と同じく呼び出し元における参照のコピーとなります。
つまり、swapメソッドでxyの参照を入れ替えたとしても呼び出し元abの参照は変更されません。
これはすなわち参照の値渡しということなります。

でも、オブジェクトの場合参照を引数に渡してるんだから参照渡しっていってもいいんじゃない?

以降は余談です。

厳密な意味での参照渡しって、たぶんC++でいうところの参照を引数として受け渡しすることだと思うんです。

でも、私が大昔すこしだけC言語をかじったときも引数の値渡し、参照渡しっていい方をしていた記憶があって、C言語の参照渡しってポインタを引数として渡すことだと思うですが、これも厳密にいうとポインタの値渡しです。

正直なところ、C言語での文脈とか理解が曖昧なのですが、もしポインタの値渡しを参照渡しという文脈があるなら、Javaのオブジェクトだって、参照渡しといってもいいんじゃない?

自分なりの結論

厳密にはJavaは値渡しである。
でも、暴論めいたことを言うと、参照渡しと言ってそれも間違ってはないっしょ。

本当に大切なのは

それが値渡しだろうが参照渡しだろうが、Javaに関してはオブジェクト引数にわたす場合その参照が渡されることには違いない。

参照の向き先はメソッドの呼び出し元、メソッド内でもおなじになるので、メソッド内で参照介してオブジェクトを操作した場合、呼び出し元に副作用的に影響が及び得る、ということです。