기본 콘텐츠로 건너뛰기

Java 람다함수에서 외부지역변수를 수정할 수 없는 이유

 C++ 하다 보면, 람다함수에서 외부지역변수 수정을 숨쉬듯이 하는 편인데, Java 에서 동일한 형태로 람다함수를 사용하려고 하면, 오류난다.

// Java
import java.util.function.Consumer;

public class MyClass {
    public String getString() {
        String result = "";

        Consumer<String> lamda = (String name) -> {
            result = "<<" + name + ">>";
        };

        return result;
    }

    public static void main(String[] args) {
        System.out.println("new MyClass().getString(): " + new MyClass().getString());
    }
}
// 컴파일 결과
// javac MyClass.java
// MyClass.java:8: error: local variables referenced from a lambda expression must be final or effectively final
//             result = "<<" + name + ">>";
//             ^
// 1 error
// C++
#include <iostream>
#include <string>
using namespace std;

class MyClass {
    public:
        string getString() {
            string result;

            auto lamda = [&] (string name) {
                result = "<<" + name + ">>";
            };

            lamda("Purewell");

            return result;
        }
};

int
main(int argc, char* argv[]) {
    cout << "(new MyClass())->getString()): " << (new MyClass())->getString() << endl;
}

// 실행결과: (new MyClass())->getString()): <<Purewell>>

위와 같이 C++에선 지역변수에 접근해서 값을 수정하였으나, Java에선 컴파일 타임에 문법 오류 난다.

사유는 Java에서 적극 사용하는 Thread인 듯 싶다.

지역변수는 Java에서 Thread끼리 공유하지 않는 Stack영역에 있고, 클래스 멤버변수들은 공유 가능한 Heap 영역에 있어서...라는데, 난 잘 모르겠다.

지역변수는 해당 메서드가 끝나면, 스택에서 사라진다. 만약 람다함수가 스레드를 사용하는 객체라면, 호출한 메서드(MyClass.getString())가 끝나도, 람다함수가 실행 중일 수 있다. 이때 캡쳐한 지역변수를 변경하려고 접근하려면, 이미 스택에서 사라진 변수에 접근하면서 오류가 발생할 것이다.

C++ 식이라면, 알아서 척척척 스스로 개발자가 모두 처리를 해야하지만, Java는 그런 경우를 아예 용납하지 않는 것 같다.

참조: https://www.baeldung.com/java-lambda-effectively-final-local-variables


댓글

이 블로그의 인기 게시물

Bash Array, Map 정리

Bash에서 Array, Map에 대한 정리. (매번 찾기 귀찮) 찾아보진 않았지만, Bash에서 Array든 Map이든 동일하게 Map(C++에서 Unordered Map)으로 동작하는 것 같다. 왜냐하면, Array의 Index가 연속하지 않아도 동작한다. 그저 Key가 0 이상의 정수인 Map이랑 비슷하게 동작한다. 예) 1, 2, 3, 9, 10 Array # 생성 declare -a empty_array declare -a ar=(haha hoho baba "long string haha hoho") # 접근 echo "ar[0]=${ar[0]}" echo "all as array=${ar[@]}" # 큰따옴표 안에서 각 원소를 따로따로 전달한다. echo "all as one=${ar[*]}" # 큰따옴표 안에서 각 원소를 문자열 하나로 합쳐 전달한다. echo "indexes=${!ar[@]}" echo "indexes=${!ar[*]}" echo "length=${#ar[@]}" echo "length=${#ar[*]}" echo "last=${ar[-1]}" echo "last=${ar[@]: -1}" # 콜론 뒤에 빈 칸이 꼭 필요하다. 옛 방식 # 현재 상황 declare -p ar #(출력) declare -a ar=([0]="haha" [1]="hoho" [2]="baba" [3]="long string haha hoho") ar[100]=hello # 인덱스를 건너 뛰어도 동작한다. declare -p ar #(출력) declare -a ar=([0]="haha" [1]="hoho" [2]="baba" [3]=...

설치한 패키지에서 RPM 추출하기

오래된 패키지를 관리할 저장소가 없어졌고, 기존 패키지로 다른 서버를 세팅해야할 일이 생겼다면 RPM의 리패키지 기능을 이용해보자. $ rpm -e --repackage [PACKAGE_NAME] 위와 같이 리패키지하면, /var/spool/repackage/ 에 생성한 RPM파일이 있다. :-)