프로그래밍 언어에서 메서드를 호출하는 경우에서 변수를 메서드의 인자로 넘길때 방식이 다양하게 있다.
Call by Value
: 변수의 '값'을 넘겨 주는 호출 방식Call by Reference
: 변수의 '참조값' (혹은 주소, 포인터)를 넘겨 주는 호출 방식Call by Assignment
: 파이썬에서 사용되는 개념으로 객체의 mutablity
에 따라 다르게 작용자바는 Call by Value 방식으로 동작하게 된다고 한다. 아래 예시를 통해 천천히 살펴보자
메서드가 호출되면 임시의 공간이 생성된다.
메서드 호출시 전달되는 변수의 값을 복사하여 전달한다
public class playGround {
private void swap(int a, int b) {
System.out.println("------메서드------");
System.out.println("인자의 값");
System.out.println("a = " + a);
System.out.println("b = " + b + "\n");
int tmp = a;
a = b;
b = tmp;
System.out.println("변경된 값");
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("---------------\n");
}
public void play() {
int a = 10;
int b = 20;
swap(a, b);
System.out.println("메서드 호출 후 값");
System.out.println("a = " + a);
System.out.println("b = " + b + "\n");
}
}
------메서드------
인자의 값
a = 10
b = 20
변경된 값
a = 20
b = 10
----------------
메서드 호출 후 값
a = 10
b = 20
간단한 메서드로도 Call by Value
임을 확인할 수 있다.
swap(a, b)
를 통해 메서드를 호출할 때, swap()
메서드 내부에서 임이의 공간을 할당받아 a와 b의 값만 복사하여 로직을 수행하고, 종료되면 없어지기 때문에 기존 a, b의 값의 변경은 일어나지 않는다.
이러한 호출방식은 기본 자료형에서 발생되며 너무나도 쉽게 접할 수 있는 메서드의 특징이다.
Java에서 객체를 생성하면, 그 객체를 참조하는 참조변수를 통해 접근할 수 있다.
그렇다면, 인자로 객체를 전달해준다면 swap()
로직이 원하는 데로 수행될까?
public class playGround {
private void swap(int a, int b) {
System.out.println("------메서드------");
System.out.println("인자의 값");
System.out.println("a = " + a);
System.out.println("b = " + b + "\n");
Integer tmp = a;
a = b;
b = tmp;
System.out.println("변경된 값");
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("---------------\n");
}
public void play() {
Integer a = 10;
Integer b = 20;
swap(a, b);
System.out.println("메서드 호출 후 값");
System.out.println("a = " + a);
System.out.println("b = " + b + "\n");
}
}
------메서드------
인자의 값
a = 10
b = 20
변경된 값
a = 20
b = 10
----------------
메서드 호출 후 값
a = 10
b = 20
int
(기본 자료형)를 Integer
(래퍼클래스)로 변경하여 로직을 수행하였지만 역시 play()
의 값은 변경되지 않았다.
좀더 객체에 가까운 예시로 변경해보자.
class Person {
String name;
public Person(String name) {
this.name = name;
}
public String printName() {
return name;
}
}
public class playGround {
private void swap(Person a, Person b) {
Person tmp = a;
a = b;
b = tmp;
System.out.println("메서드 로직 수행 후");
System.out.println("a = " + a.printName());
System.out.println("b = " + b.printName() + "\n");
}
public void play() {
Person a = new Person("A");
Person b = new Person("B");
System.out.println("메서드 호출 전");
System.out.println("a = " + a.printName());
System.out.println("b = " + b.printName() + "\n");
swap(a, b);
System.out.println("메서드 호출 후");
System.out.println("a = " + a.printName());
System.out.println("b = " + b.printName());
}
}
메서드 호출 전
a = A
b = B
메서드 로직 수행 후
a = B
b = A
메서드 호출 후
a = A
b = B
동일한 결과다.
메서드 호출 전 a = A라는 이름을, b = B라는 이름을 부여 하였고, 메서드의 인자로 객체를 넘겨주었지만, 메서드가 종료된 후 다시 출력해 보면 변경되지 않았다는 점을 알 수 있다.
이유는, Call by Value
에서 찾을 수 있다.
객체를 넘겨주는 경우에도, 메서드에서는 임시 공간을 할당하여 넘겨받은 객체의 참조값을 복사하여 저장하기 때문이다. 하나의 객체를 참조하는 변수가 새로 생성된 것이라 보면 될 것이다.
class Count {
int count;
}
public class playGround {
private void countUp(Count a) {
a.count++;
}
public void play() {
Count a = new Count();
System.out.println("카운트 업 전");
System.out.println("a.count = " + a.count + "\n");
countUp(a);
System.out.println("카운트 업 후");
System.out.println("a.count = " + a.count);
}
}
카운트 업 전
a.count = 0
카운트 업 후
a.count = 1
메서드에서 넘겨받은 객체의 참조값을 복사하여 사용하기 때문에 위와 같은 코드는 의도했던 데로 수행된다.
Java를 사용하는데 있어 Call by value인지 Reference인지를 명확히 구분하는 것은 중요하지 않은 것 같다. 이번 공부를 통해 중요히 기억해야할 점은, 객체를 메서드로 넘기는 경우 참조형을 직접 전달하는 것이 아닌, 그 참조값을 복사해서 사용한다는 것이다.