brunch

You can make anything
by writing

C.S.Lewis

by 강관우 Mar 07. 2016

자바의 아규먼트 전달 방식

 call-by-value일까요, call-by-reference일까요?


자바는 call-by-value를 지원하나요? 아니면 call-by-reference를 지원하나요?




많은 분들이 위 질문에 명확한 답을 못 내리시거나, 두 가지 방법에 대한 차이점을 모르고 계실 수도 있습니다. 

하지만 이 질문은 자바로 어떤 로직을 구현함에 있어 정말 중요한 요소이므로 짚고 넘어가시는 게 좋습니다.


call by value와 call by reference 제대로 알기


위키에 나와있는 '값에 의한 호출'의 설명은 다음과 같습니다.

In call-by-value, the argument expression is evaluated, and the resulting value is bound to the corresponding variable in the function (frequently by copying the value into a new memory region). If the function or procedure is able to assign values to its parameters, only its local copy is assigned — that is, anything passed into a function call is unchanged in the caller's scope when the function returns.

위 내용에 따르면, call-by-value 메커니즘은 함수로 인자를 전달할 때 전달될 변수의 값을 복사하여 함수의 인자로 전달한다고 하고 있습니다. 또한 복사된 값은 함수 내에서 지역적으로 사용되는 local value라는 특징을 가지고 있습니다. 그리고 caller는 인자값을 복사 방식으로 넘겨주었으므로, callee 내에서 어떤 작업을 하더라도 caller는 영향을 받지 않습니다. 

Call-by-value의 대표적인 예제 코드가 C언어에서 포인터를 설명할 때 등장하는 swap 함수입니다.

```

void swap( int target1,  int target2) {

    int tmp = target1;

    target1 = target2;

    target2 = tmp;

}

int main(int argc, char** argv) {

    int x = 10, y = 20;

    swap(x, y);

    printf("x = %d, y = %d\n", x, y);

    return;

}

```

결과 

>$ 10 , 20


swap 함수를 수행했음에도 불구하고 왜 x와 y의 값은 변경되지 않았을까요? 이는 swap 함수에 아규먼트가 전달될 때, x, y의 값을 복사해서 값만 넘겨주기 때문입니다. wap 메소드 안에 메모리 영역에 새로운 x, y 변수가 생성된다는 말이죠. 이 내용이 'call by value', 즉 값에 의한 호출의 핵심입니다.


그럼 call-by-reference에 대한 정의를 살펴보도록 합시다. 

Call-by-reference
In call-by-reference evaluation(also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify (i.e. assign to) the variable used as argument—something that will be seen by its caller.

Call-by-reference는 인자로 사용될 변수의 레퍼런스를 함수로 전달하며, 그것이 변수의 값은 아니다라고 돼있습니다. 이해를 돕기 위해 이전 swap 함수의 예를 통해 설명해본다면, call-by-reference로 swap 함수에 뭔가를 전달한다면 x와 y가 가지고 있는 값(10, 20)이 아닌 x, y 변수 자체에 대한 레퍼런스를 전달해야 합니다.

C언어는 포인터로 call-by-reference를 구현할 수 있습니다.


```

void swap( int* target1,  int* target2) {

    int tmp = *target1;

    *target1 = *target2;

    *target2 = tmp;

}

int main(int argc, char** argv) {

    int x = 10, y = 20;

    swap($x, $y);

    printf("x = %d, y = %d\n", x, y);

    return;

}

```

결과 

>$ 20 , 10


위 코드는 x,y의 값이 바뀌었네요. 바로 swap 함수에 x, y가 저장된 곳 자체를 넘겼기 때문입니다. 

바로 이렇게 함수의 아규먼트로 레퍼런스를 넘겨서 변경 사항을 함수를 호출한 곳에서도 적용되게끔 하는 방식이 바로 'call by reference'입니다.


그렇다면 이쯤에서 다시 질문을 봐볼까요?

자바는 call-by-value를 지원하나요? 아니면 call-by-reference를 지원하나요?

 call-by-value는 아규먼트로 전달되는 객체의 값을 복사하여 전달하므로 함수를 호출한 쪽에 변수는 함수 내에서 일어난 작업의 영향을 받지 않는 방식입니다.

 자바에서 call-by-value가 어떻게 일어나는지 확인할 수 있는 예제와 함께하겠습니다.

 우선 예제에 사용될 Person 클래스를 정의하겠습니다.


public class Person {    

    private String name;        

    public Person(String name) {

        this.name = name;    

    }    

    public void setName(String name) {

         this.name = name;     

    }    

    @Override    

    public String toString() {        

        return "name is " + this.name;   

    }

}


다음은 Person 객체를 넘겨받아 새로운 Person 객체로 변경하는 메서드 호출 코드입니다.


public class CallByValueTest {    

    public static void assignNewPerson(Person p) {        

        p = new Person("장비");    

    }

    public static void main(String[] args) {

        Person gwanwoo = new Person("관우");        

        assginNewPerson(gwanwoo);        

        System.out.println(gwanwoo);        

        System.exit(0);    

    }

}


결과

> $ name is 관우


gwanwoo 객체를 생성한 후 changePerson 메서드로 전달하고 메서드 종료 후 gwanwoo 객체가 다른 객체로 변경되었는지 확인해 봤습니다. 결과는 아무런 변화가 없습니다. 그럼 call-by-value일까요? 

결론부터 말씀드리면, 맞습니다. 자바는 언제나 'call-by-value'입니다. 하지만 다음 예제를 보고도 이렇게 확신할 수 있을까요?


public class CallByXXXTest {    

    public static void changePersonName(Person p) {        

        p.setName("장비");    

    }    

    public static void main(String[] args) {

        Person gwanwoo = new Person("관우");        

        changePersonName(gwanwoo);        

        System.out.println(gwanwoo);        

        System.exit(0);    

    }

}

 결과

> $ name is 장비


 잉?? 자바 프로그래밍을 해보신 분들이라면 너무나 당연한 결과인데, call-by-xxx를 공부하면서 보니까 이상하네요. 자바는 call-by-value인데! 왜 함수 안에 로직이 아규먼트의 값을 변경시켰을까요?

 문제는 value라는 용어의 정의에서 발생합니다.

 저희의 능동적이지 못한 두뇌 회전을 혼내시려고 작심하신 듯 '제임스 고슬링'형은 이 value의 메커니즘을 변수의 타입에 따라 다르게 적용되게끔 해버리셨습니다.   

 

 자바의 자료형은 기본자료형과 참조자료형이 있습니다.

[이전 포스트 참고 : (https://brunch.co.kr/@kd4/1)]

 자바는 기본자료형에 해당하는 변수를 아규먼트로 넘길 때는 값을 넘기고, 참조자료형인 변수를 아규먼트로 넘길 때는 레퍼런스를 넘기는 것처럼(?) 보이게 되어 있습니다. 

 정확히 말하면 매개변수로 넘어오는 변수가 가리키는 인스턴스 메모리 주소를 넘겨주므로 call by value라고 할 수 있습니다. 


assignNewPerson 함수에서 gwanwoo라는 변수가 가리키는 "관우" Person 인스턴스 주소를 넘기긴 했지만 gwanwoo 변수 자체의 레퍼런스를 넘긴 것은 아니기때문에 p 변수에 새로운 person을 할당하더라도 gwanwoo 변수에 영향은 없습니다. "변수"의 레퍼런스와 "인스턴스"의 레퍼런스를 구별해야합니다. 


https://stackoverflow.com/a/12429953



이렇듯 자바는 객체의 타입에 따라 값만 넘기는 것 같기도 하고~ 레퍼런스를 넘기는 것 같기도 합니다. 


이 개념은 깊이 들어갈수록 좋으니까 더 깊은 공부를 원하시면 아래 사이트를 참고하면 좋습니다.


http://mussebio.blogspot.kr/2012/05/java-call-by-valuereference.html 


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari