Java는 call by value일까? call by reference일까?
업데이트:
Java는 call by value일까? call by reference일까?
- 결론부터 말하자면 Java는
call by value!
- 그 이유에 대해 예시를 작성하면서 알아볼 예정이다.
C++에서의 call by value vs call by reference
- call by value냐 call by reference냐를 설명하기 위한 가장 대표적인 예시는 단연 swap 함수일 것이다.
-
먼저
call by value
의 아주 대표적인 예시이다.#include<stdio.h> void swap(int a, int b) { int tmp = a; a = b; b = tmp; } int main() { int a = 10, b = 20; printf("swap 함수 호출 전... a: %d, b: %d\n", a, b); swap(a, b); printf("swap 함수 호출 후... a: %d, b: %d\n", a, b); }
-
결과는 아래와 같다.
swap 함수 호출 전... a: 10, b: 20 swap 함수 호출 후... a: 10, b: 20
- 일반적인 값이 들어있는 변수를 파라미터로 전달했을 때, 파라미터로 전달한 메서드 내에서 값을 바꾸더라도,
원본값은 변하지 않는다.
- 이유는 함수를 호출할 때 파라미터 값이
복사
되어 전달되기 때문이다. - 그렇다면, 주소값을 파라미터로 넘겨 swap 함수를 호출하면 어떻게 될까?
-
다음으로
call by reference
의 예시이다.#include<stdio.h> void swap(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } int main() { int a = 10, b = 20; printf("swap 함수 호출 전... a: %d, b: %d\n", a, b); swap(&a, &b); printf("swap 함수 호출 후... a: %d, b: %d\n", a, b); }
- 결과는 다음과 같다.
swap 함수 호출 전... a: 10, b: 20 swap 함수 호출 후... a: 20, b: 10
- 위와 같이 a,b의 값이 변경되는 것을 확인할 수 있다!
- 즉, 주소값을 파라미터로 넘겨 함수를 호출하면, 함수를 호출한 쪽의 원본 값을 바꿀 수 있고, 이런 방식으로 함수를 호출하는 방식을
call by reference
라고 한다.
그렇다면 Java에서는??
- 자바의 변수에는 크게 primitive type과 reference type이 있다.
-
먼저
primitive type
을 파라미터로 전달하는 예시를 살펴보자.package test; public class Test { public static void swap(int a, int b) { int tmp = a; a = b; b = tmp; } public static void main(String[] args) { int a = 10, b = 20; System.out.printf("swap 함수 호출 전... a: %d, b: %d\n", a, b); swap(a, b); System.out.printf("swap 함수 호출 후... a: %d, b: %d\n", a, b); } }
- 결과
swap 함수 호출 전... a: 10, b: 20 swap 함수 호출 후... a: 10, b: 20
- 역시나 예상대로 swap함수를 호출하더라도 원본값이 변하지 않는다.
-
왜 그럴까? 자바의 메모리 구조를 통해 더 깊게 파고들어보자.
- swap 함수가 호출되면, main thread내에 swap frame이 새로 생성되어 파라미터로 전달된 a,b에는 값이 복사되어 전달된다.
- swap 함수가 종료되면, swap frame이 사라지고 swap frame의 변수들도 같이 사라진다.
- 그렇기 때문에
main frame의 변수에는 아무런 영향을 줄 수 없다.
- 그렇다면 reference type을 파라미터로 전달해 해당 메서드 내에서 값을 변경하면 어떻게 될까?
-
reference type으로 선언된 참조 변수에는 참조값이 들어가니까, 값이 변하지 않을까?
package test; class Player { private String name; private int salary; private int age; public Player(String name, int salary, int age) { this.name = name; this.salary = salary; this.age = age; } public void setName(String name) { this.name = name; } public void setSalary(int salary) { this.salary = salary; } public void setAge(int age) { this.age = age; } public String getName() { return name; } } public class Test { public static void changePlayer(Player player) { player = new Player("Messi", 1000000000, 35); } public static void main(String[] args) { Player player = new Player("Yang", 1000000, 25); System.out.println("changePlayer 메서드 호출 전 player는 " + player.getName() + " 입니다."); changePlayer(player); System.out.println("changePlayer 메서드 호출 후 player는 " + player.getName() + " 입니다."); } }
-
결과
changePlayer 메서드 호출 전 player는 Yang 입니다. changePlayer 메서드 호출 후 player는 Yang 입니다.
- 예상과는 달리 player는 변하지 않았다.
- 왜 바뀌지 않았을까?
-
다시 메모리 구조를 그려가면서 이해해보자!
- 위와 같이 changePlayer 메서드가 호출되면 main thread 내에 changePlayer-frame이 생성된다.
- 그리고 매개변수로 참조값이 복사되어 전달된다.
- 이후, changePlayer의 파라미터에 새로운 객체 (
new Player("Messi", 1000000000, 35);
)를 할당하면 위와 같이 그려질 것이다. - 다음으로, changePlayer가 종료된 후 main 메서드 내의 player의 참조값이 새롭게 변할까?
- 변하지 않는다! 왜그럴까?
- changePlayer가 종료되면 changePlayer-frame은 소멸되고, 마찬가지로 main-frame의 player 변수에 아무런 영향을 주지 못하기 때문이다.
결론
- Java에서는
메소드의 파라미터로 전달한 객체를 호출된 메소드안에서 변경을 할 수 없다.
- 누군가는 setter를 이용해 객체를 변경할 수 있지 않느냐? 라고 의문을 제기할 수도 있을 것이다.
- 하지만, setter를 이용해 객체를 변경하는 것은 해당 객체의 속성을 변경하는 것이지 객체 자체를 변경하는 것은 불가능하다.
- 그렇기 때문에
Java는 call by value
이다.
댓글남기기