メソッドの仮引数に対する操作と呼び出し元への影響

[Java8の言語仕様4.12.5 Initial Values of Variables](http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.5)に変数の初期値について定義されてます。そこではメソッドのパラメータに対して、以下のように説明されています。

> Each method parameter (§8.4.1) is initialized to the corresponding argument value provided by the invoker of the method (§15.12).

上記の通り、メソッドを呼び出したとき、仮引数は対応する実引数で初期化されます。

この記事ではメソッドの引数に変数を指定した場合を考えます。呼び出し先で仮引数に対してどのような操作をすると、呼び出し元の変数に影響があるのかを紹介します。

#準備

メソッドの仮引数にint型、配列型、クラス型を使います。クラス型は以下のPersonクラスを使用します。

```java:Personクラス
public class Person {

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}
```


#影響しない操作

仮引数に代入操作をしても、呼び出し元には影響しません。以下、具体例と結果です。

```java:例
public class TestAssignment {

    public static void main(String[] args) {

        int n = 1;
        Person person = new Person("test1", 10);
        int[] nums = {1, 2, 3};

        // メソッド呼び出し前
        System.out.println("呼び出し前");
        System.out.println("n: " + n);
        System.out.println("person: " + person.toString());
        System.out.println("nums: " + Arrays.toString(nums));
        System.out.println();

        // メソッド呼び出し
        method(n, person, nums);

        // メソッド呼び出し後
        System.out.println("呼び出し後");
        System.out.println("n: " + n);
        System.out.println("person: " + person.toString());
        System.out.println("nums: " + Arrays.toString(nums));
        System.out.println();

    }

    public static void method(int a, Person p, int[] intArray) {

        // メソッド内の仮引数の初期値
        System.out.println("メソッド内の仮引数初期値");
        System.out.println("a: " + a);
        System.out.println("p: " + p.toString());
        System.out.println("intArray: " + Arrays.toString(intArray));
        System.out.println();

        a = 10;
        p = new Person("name2", 20);
        intArray = new int[2];
        intArray[0] = 5;
        intArray[1] = 6;

        // メソッド内で仮引数に格納後
        System.out.println("メソッド内で仮引数に格納後");
        System.out.println("a: " + a);
        System.out.println("p: " + p.toString());
        System.out.println("intArray: " + Arrays.toString(intArray));
        System.out.println();

    }

}
```

```:結果
呼び出し前
n: 1
person: Person [name=test1, age=10]
nums: [1, 2, 3]

メソッド内の仮引数初期値
a: 1
p: Person [name=test1, age=10]
intArray: [1, 2, 3]

メソッド内で仮引数に格納後
a: 10
p: Person [name=name2, age=20]
intArray: [5, 6]

呼び出し後
n: 1
person: Person [name=test1, age=10]
nums: [1, 2, 3]
```

メソッド内では仮引数に代入操作をしていますが、呼び出し前と呼び出し後で変数の出力結果が同じです。

#影響する操作

##配列の場合

配列要素に対して代入操作をすると呼び出し元の値も変更されます。以下、具体例と結果です。

```java:例
public class TestArrayType {

    public static void main(String[] args) {

        int[] nums = {1, 2, 3};

        // メソッド呼び出し前
        System.out.println("呼び出し前");
        System.out.println("nums: " + Arrays.toString(nums));
        System.out.println();

        // メソッド呼び出し
        method(nums);

        // メソッド呼び出し後
        System.out.println("呼び出し後");
        System.out.println("nums: " + Arrays.toString(nums));
        System.out.println();
    }

    public static void method(int[] intArray) {

        // メソッド内の仮引数の初期値
        System.out.println("メソッド内の仮引数初期値");
        System.out.println("intArray: " + Arrays.toString(intArray));
        System.out.println();

        intArray[0] = 5;
        intArray[1] = 6;
        intArray[2] = 7;

        // メソッド内で仮引数に格納後
        System.out.println("メソッド内でpのsetter呼び出し後");
        System.out.println("intArray: " + Arrays.toString(intArray));
        System.out.println();

    }

}
```

```:結果
呼び出し前
nums: [1, 2, 3]

メソッド内の仮引数初期値
intArray: [1, 2, 3]

メソッド内でpのsetter呼び出し後
intArray: [5, 6, 7]

呼び出し後
nums: [5, 6, 7]
```

呼び出し前と呼び出し後で配列numsの内容が変わってます。

##クラスに対してメソッドを使った場合

クラスに対してフィールドを変更するメソッドなどを使った場合、呼び出し元のクラスの内容も変更されます。以下、具体例と結果です。

```java:例
public class TestClassType {

    public static void main(String[] args) {

        Person person = new Person("test1", 10);

        // メソッド呼び出し前
        System.out.println("呼び出し前");
        System.out.println("person: " + person.toString());
        System.out.println();

        // メソッド呼び出し
        method(person);

        // メソッド呼び出し後
        System.out.println("呼び出し後");
        System.out.println("person: " + person.toString());
        System.out.println();
    }

    public static void method(Person p) {

        // メソッド内の仮引数の初期値
        System.out.println("メソッド内の仮引数初期値");
        System.out.println("p: " + p.toString());
        System.out.println();

        p.setName("test2");
        p.setAge(20);

        // メソッド内で仮引数に格納後
        System.out.println("メソッド内でpのsetter呼び出し後");
        System.out.println("p: " + p.toString());
        System.out.println();

    }

}
```

```:結果
呼び出し前
person: Person [name=test1, age=10]

メソッド内の仮引数初期値
p: Person [name=test1, age=10]

メソッド内でpのsetter呼び出し後
p: Person [name=test2, age=20]

呼び出し後
person: Person [name=test2, age=20]
```

呼び出し前と呼び出し後で、変数personの内容が変わっています。

#Java SE APIの例

2つの例を紹介します。

Arrays#fill()メソッドは第一引数が配列です。内部で第一引数の配列要素に代入操作をしています。

```java
    public static void fill(int[] a, int val) {
        for (int i = 0, len = a.length; i < len; i++)
            a[i] = val;
    }
```

Collections#addAll()メソッドは第一引数がCollectionです。内部で第一引数に対してadd()メソッドを呼び出しています。

```java
    public static <T> boolean addAll(Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements)
            result |= c.add(element);
        return result;
    }
```