오늘도 역시 java에 대해서 이야기 해볼려고 한다. java로 먹고 살아야 하니..

call by value와 call by reference

오늘 내용은 제목에도 있다시피 java는 call by value?, call by reference? 를 알아볼 예정이다. 그 전에 아주 간단하게 call by value와 call by reference에 대해서 살짝 살펴보자. 보통 c 보다 자바를 먼저 공부한 개발자는 들어본 개발자도 있을 것이고 듣지 못한 개발자도 있을 수 있다. 왜냐하면 책에서 딱히 다루지 않기 때문이다. 필자도 자바책에서는 거의 못본.. 사실 자바만 다루는책은 두권정도밖에 없다. 그래서 못본거일 수도.. 하지만 c와 c++을 먼저 공부를 했다면 무조건 등장한다. 잠시 c언어 책을 꺼내고 살펴보도록 하자.

call by value (값에 의한 호출)

call by value는 가장 일반적인 함수 호출형태로 값을 복사하는 것이다. 예를 들어 보자.

int add(int a, int b) 
{
    return a + b;
}

c 의 함수의 기본적인 형태이다. (혹시 다음에 나올 문법이 c++이라도 그냥 c라고 이야기 하겠다.) add 메서드를 호출하면 a + b를 더한 값을 리턴한다. 호출 해보자.

int a1 = 10;
int a2 = 20;
cout << add(a1, a2) << endl;

값은 당연히 30이 출력 될 것이다. 여기서 변수 a1과 a2는 add() 함수의 a, b와는 완전 별개의 변수가 된다. 즉 값이 복사되는 것이다. 그래서 만약 add 함수의 a와 b를 변경 하더라도 a1와 a2는 영향을 받지 않는다. 뭐 당연한 말을 이렇게 하나 싶은데 좀 더 살펴보자.

call by value와 call by reference가 나오면 단골로(?) 나오는 예제를 살펴보자. 예상했겠지만 swap함수를 만들어보자.

void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

뭐 어려운 부분은 없다. 두 변수를 바꾸는 함수이다. 한번 호출해보자.

int a1 = 10;
int a2 = 20;
swap(a1, a2);
cout << "a1: " << a1 << " a2: " << a2 << endl;

출력 해보면 a1: 10 a2: 20 이와 같이 출력 될 것이다. 아까 위에서 a1과 a는 별개의 변수인 것이 증명 되었다. 뭔 당연한 소리를 이렇게 길게 이야기 하고 있어? 라고 할 수도 있으니 빨리 다음으로 넘어가자.

call by reference (참조의 의한 호출)

call by reference 를 자세히 이야기하면 글이 너무 길어지므로 간단하게만 이야기 해보자. 포인터에 대해서도 잘알고 해야되는데 여기에서는 그게 중요한 내용이 아니므로 모르는 사람이 있다면 그냥 이런게 있다고만 알고 있자.

간단하게 한줄로 요약하자면 변수의 주소를 전달하는 것이다. 다시 swap 함수를 call by reference 로 만들어보자.

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

int a1 = 10;
int a2 = 20;
swap(&a1, &a2);
cout << "a1: " << a1 << " a2: " << a2 << endl;

호출하면 아까 위의 call by value 와는 다르게 a1: 20 a2: 10 두개의 변수가 바뀌어 출력 된다. 이게 바로 call by reference 참조의 의한 호출이다. 포인터 변수 a와 b에는 각각의 a1의 주소 a2의 주소가 전달된다. call by value 처럼 값을 복사하는 것이 아니고 실제 주소를 전달하여 swap 함수 내에서 조작을 하면 실제 값도 변경 될 수 있는 것이다.

이정도면 아주 정확히는 몰라도 어느정도 두개의 차이점을 알아봤다. 그렇다면 과연 java는 call by reference 일까?

java는 call by reference가 아니다.

결론부터 말하자면 java는 항상 call by value이다. 흔히 java의 오해를 살 수 있는 부분을 살펴보자.

public class CallByValue {

  public static void main(String[] args) {
    Person p = new Person("wonwoo");
    System.out.println("p.name: " + p.name);
    callByValue(p);
    System.out.println("p.name: " + p.name);
  }

  public static void callByValue(Person p) {
    p.name = "kevin";
  }
}

class Person  {
  String name;

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

위의 코드를 출력해보면 아래와 같이 출력 된다.

p.name: wonwoo
p.name: kevin

읭? 바뀌었는데? 그럼 자바도 call by reference가 아닐까? 오해의 소지는 여기서 발생했다. 실제 상태값을 바꾸는 것에서 오해가 시작되었다. call by reference라면 상태를 변경하는게 아니라 실제 callByValue 함수의 p에 다른 Person 객체를 넣어 바뀐다면 그게 call by reference가 되는 것이다. 다음과 같이 말이다.

public class CallByValue {

  public static void main(String[] args) {
    Person p = new Person("wonwoo");
    System.out.println("p.name: " + p.name);
    callByValue(p);
    System.out.println("p.name: " + p.name);
  }

  public static void callByValue(Person p) {
    p = new Person("kevin");
  }
}

class Person  {
  String name;

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

callByValue 메서드부분만 바뀌었다. callByValue 메서드를 보면 이름을 kevin으로 다시 생성해서 할당한다. 만약 자바가 call by reference라면 아까와 동일하게 출력 되어야만 한다. 하지만 이 코드를 출력해보면 다음과 같다.

p.name: wonwoo
p.name: wonwoo

실제로 Person은 변경되지 않았다. call by reference가 아닌 또다른 이유는 자바에서는 객체의 주소를 가져오는 방법이 없다. 만약 call by reference 지원한다면 주소를 가져오는 방법을 지원해야 할 것인데 말이다.

그럼 우리는 call by reference가 어떤건지 보자. c는 call by reference 를 지원하니 c코드를 보자. 정확히는 c++.. 오랜만에 c로 코딩할려니 문법조차 다 까먹었다.. Xcode도 거의 4년? 5년만에 열었..

#include <iostream>

using namespace std;

class Person 
{

    public :
    string name;

    Person (string name) {
        this->name = name;
    }
};

int add(int a, int b)
{
    return a + b;
}

void callByReference(Person *p)
{
    *p = Person("kevin");
}

void callByValue(Person p)
{
    p = Person("kevin");
}

int main(int argc, const char * argv[]) 
{
    Person p("wonwoo");
    cout << "p.name: " << p.name << endl;
    callByReference(&p);
//    callByValue(p);
    cout << "p.name: " << p.name << endl;
    return 0;
}

아까 자바와 동일하게 만들었다. 실제 callByReference() 함수를 호출하면 실제 바뀐 내용이 출력 되지만 callByValue() 함수를 호출할 경우에는 자바와 동일하게 wonwoo가 두 번 출력 된다. 이로써 java는 call by reference가 아니라는게 증명? 되었을 것이라고 생각된다.

오늘은 이렇게 자바의 call by value와 call by reference(?) 에 대해서 알아봤다. 만약 call by value와 call by reference에 대해서 더 자세히 알고 싶다면 인터넷으로 찾길 바란다. 필자는 역량은 여기까지다.. ㅜㅜ
자바가 call by reference라는 오해를 할 수 있다. 필자 역시 초보 시절에는 자바가 call by reference가 되는 줄 알았기 때문이다. 어떻게 읽고 쓰냐에 대해서 오해의 소지가 있을 수 있으니 조심해야 될 듯 싶다.

오늘은 여기서 이만..