
In a programming language, an evaluation strategy is a set of rules for evaluating expressions.[1] The term is often used to refer to the more specific notion of a parameter-passing strategy[2] that defines the kind of value that is passed to the function for each parameter (the binding strategy)[3] and whether to evaluate the parameters of a function call, and if so in what order (the evaluation order).
| Evaluation strategy | Representative Languages | Year first introduced |
|---|---|---|
| Call by reference | FORTRAN II, PL/I | 1958 |
| Call by value | ALGOL, C, Scheme, MATLAB[9] | 1960 |
| Call by name | ALGOL 60, Simula | 1960 |
| Call by copy-restore | Fortran IV, Ada[10] | 1962 |
| Call by need | SASL,[11] Haskell, R[12] | 1971[13] |
| Call by sharing | CLU, Java, Python, Ruby, Julia | 1974[14] |
| Call by reference parameters | C++, PHP,[15] C#,[16] Visual Basic .NET[17] | 1985[18] |
| Call by reference to const | C++, C | 1985[18] |
프로그래밍 언어에서 평가 전략이란 함수나 메서드에 인자를 전달하는 방식을 의미합니다. 대표적으로 널리 알려진 것이 Call-By-Reference, Call-By-Value가 있죠.
#include <iostream>
using namespace std;
void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
}
int main() {
int a = 10;
int b = 20;
cout << "a : " << a << endl;
cout << "b : " << b << endl;
swap(a, b);
cout << "after swap" << endl;
cout << "a : " << a << endl;
cout << "b : " << b << endl;
}
Result
a : 10
b : 20
after swap
a : 10
b : 20
C++을 배우면 나오는 함수 부분에서 나오는 예제입니다. swap 함수의 의도는 두 변수의 값을 변경하고자 하는 것인데 제대로 시행되지 않았죠.
그 이유는 바로 해당 함수는 값 만을 전달 했고, 별도의 공간에서 시행되었기 때문입니다.
#include <iostream>
using namespace std;
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
int main() {
int a = 10;
int b = 20;
cout << "a : " << a << endl;
cout << "b : " << b << endl;
swap(&a, &b);
cout << "after swap" << endl;
cout << "a : " << a << endl;
cout << "b : " << b << endl;
}
Result
a : 10
b : 20
after swap
a : 20
b : 10
위의 함수와는 다르게 이 코드에서 swap 함수는 주솟값을 전달받습니다.
그리고 주솟값에 실제로 접근하여 내부의 값을 조정하죠.
그렇게 때문에 실제로 두 값이 바뀌게 됩니다.
포인터로 유명한 C++에서는 위와 같이 해결하는 것을 알았습니다.
그렇다면 자바에서는 어떻게 값을 전달할까요?
위의 위키 백과에서도 확인할 수 있듯이 Call-By-Sharing 이라는 방식으로 값을 전달합니다.
public class Main {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
printArr(arr);
foo(arr);
printArr(arr);
goo(arr);
printArr(arr);
}
static void foo(int[] arr) {
arr[0] = 5;
}
static void goo(int[] arr) {
int[] newArr = new int[]{5, 6, 7};
arr = newArr;
}
static void printArr(int[] arr) {
System.out.println(Arrays.toString(arr));
}
}
실행 결과
19:41:29: Executing ':Main.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :Main.main()
[1, 2, 3]
[5, 2, 3]
[5, 2, 3]
BUILD SUCCESSFUL in 162ms
2 actionable tasks: 2 executed
19:41:29: Execution finished ':Main.main()'.
public class MutImut {
public static void main(String[] args) {
String str = "hello";
StringBuilder sb = new StringBuilder("hello");
System.out.println(str + ", " + sb.toString());
foo(str);
goo(sb);
hoo(sb);
System.out.println(str + ", " + sb.toString());
}
static void foo(String str) {
str = "world";
}
static void goo(StringBuilder sb) {
sb = new StringBuilder("world");
}
static void hoo(StringBuilder sb) {
sb.append("hahaha");
}
}
실행 결과
19:45:34: Executing ':MutImut.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :MutImut.main()
hello, hello
hello, hellohahaha
BUILD SUCCESSFUL in 159ms
2 actionable tasks: 2 executed
19:45:34: Execution finished ':MutImut.main()'.
값이 원하는 대로 변경될 때도 있지만, 원하는 대로 변경되지 않을 때도 존재합니다.
변경될 때도 존재하니깐 Reference 일까요?
아니면 Value?
정답은 Value 입니다! 정확히는 Call-by-Sharing 이죠. 하지만 대다수의 사람들도 그렇고 Call by Value 방식으로 칭하고 있습니다.
그러면 인자로 넘겨준 값이 변경된 경우는 어떤 경우인 것 일까요?
mutable 객체는 변경할 수 있지만, immutable 객체는 변경할 수 없습니다.
새로운 객체로 대체를 할 수도 없습니다!!!
String은 불변 객체이기 때문에 변경과 대체가 실행되지 않습니다.
StringBuilder의 경우네는 가변 객체이지만, 새로 할당한 경우(대체를 시도한 경우)에는 변경이 적용되지 않습니다!!