Dev/Java

람다 표현식 내에서 표현식 외부의 변수 사용

pu3vig 2022. 8. 5. 16:19
728x90
  • target:  Java의 람다 표현식에서의 표현식 외부의 변수 사용

 


  • method: 

1. Lambda Expression

Method를 하나의 식으로 표현한 것

Method를 람다식으로 표현할 경우, 이름/반환값이 없어지므로, 이를 익명함수(Anonymous function)이라고도 함

Method를 변수처럼 다루는 것이 가능

 


2. 지역 변수 제약

Java8에서의 람다 표현식을 사용할 때, 람다 표현식 내부에서 자유변수(람다 표현식 기준 외부의 변수)의 값을 설정할 수 없음

람다 표현식 내부에서 사용이 불가능한 것이 아니라, 외부 변수를 재정의할 수 없음을 의미

이는 js에서 const 타입과 유사성을 보이지만, const 타입은 java의 final와 동일하고, 이 effectively final 변수는 람다 표현식 내부에서 람다 캡쳐링(=Lambda Capturing)을 통해서 설정 가능

※ js에서의 const / let 타입은 ES6 문법에서 추가되었으며, 광범위한 var 타입 변수의 보완으로 접근 제어 역할이 추가됨

 


3. Effectively final

Effectively final란 final 선언이 되어있지 않으나, 재할당이 발생하지 않는 변수를 의미

// 가능
String str = "Hello World";
Runnable r1 = () -> System.out.println(str);

// 불가능(최초 선언 및 초기화 이후에 str 변수가 변경되면서 람다 식 내부에서는 사용이 불가능
//str += "!";
//Runnable r2 = () -> System.out.println(str);

// 불가능(최초 선언 및 초기화 이후 람다 식 내부에 적용 이후에 변경하여도 사용이 불가능
//Runnable r3 = () -> System.out.println(str);
//str += "!";

※ 최초 변수 선언 및 초기화 이후에 해당 변수를 변경하는 경우 람다 식 내에서 사용할 수 없음

 


4. 동작 방식

인스턴스 변수는 heap에 저장, 지역변수는 stack에 저장

stack 영역은 heap과 달리 각 thread간에 공유되지 않는 영역

자유 변수는 람다 캡쳐링에 의해 복사되기 때문에 다른 thread에서 참조할 수 있으나, 람다 표현식 실행 시점차로 인해, Sync를 맞출 수 없기에 effectively final로 쓰임

※ 클래스 변수는 thread간에 공유되는 method 영역에 저장

 


5. Lambda Capturing

람다 식은 다른 thread에서도 실행 가능

만약 A thread에서 생성한 람다 식을 B thread에서 사용할 경우, 인스턴스 변수는 thread간에 공유가 가능한 heap 영역에 저장되어 공유가 가능하지만, stack영역에 있는 지역변수는 외부 thread에서 접근이 불가능

 

따라서, stack영역에 있는 변수를 사용할 수 있도록 지역 변수의 복사본을 제공해서 람다 식에서 접근 가능하도록 설정

(= Lambda Capturing)

 

하지만, 원래 stack에 저장된 지역 변수의 값이 바뀌면 capturing한 람다 식 내부의 변수와의 동기화가 보장되지 않기 때문에 동시성 이슈가 발생

따라서 지역 변수는 final임이 보장되어야 함

 


5. 적용 방법

간혹가다가 람다 표현식 내부에서 외부 변수를 참조해야하는 경우가 생기는데(합계출력이나, 일괄 문자열 append 시)

아래와 같은 방식으로 해결가능

 

람다 표현식의 경우, Effectively final 변수를 컨트롤 할 수 없기 때문에 아래와 같은 코드는 불가능

String word = "Hello";

// array = ['W','o','r','l','d']
array.stream().forEach(item -> word += item;);

Effectively final 변수를 forEach안의 람다 표현식 내부에서 설정을 시도하기 때문에 불가능

 

대신 아래와 같은 코드는 가능

StringBuffer word = new StringBuffer();
word.append("Hello");

// array = ['W','o','r','l','d']
array.stream().forEach(item -> word.append(item););

 


  • source:

https://bcp0109.tistory.com/319

 

effectively final: 람다 표현식 내에서 사용하는 변수가 final 처럼 사용되어야 하는 이유

1. Overview Java 의 람다 표현식은 익명 함수처럼 자유 변수 (Free Variable) 를 활용할 수 있습니다. 여기서 자유변수란 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수 를 의미합니다. 2. 지역 변

bcp0109.tistory.com

https://ryan-han.com/post/dev/java-lambda/

 

자바의 정석 - 람다식(Lambda Expression) | Integerous DevLog

자바의 정석 - 람다식(Lambda Expression) 2018/11/29 자바의 정석(남궁성 저) 2권 학습내용 정리 1. 람다식 메서드를 하나의 식(expression)으로 표현한 것. 메서드를 람다식으로 표현하면 메서드의 이름과

ryan-han.com

 

728x90

'Dev > Java' 카테고리의 다른 글

[Java 8+] Stream to map 및 Method Reference  (0) 2022.08.08
Stream을 통한 Map에서의 원하는 키 추출 및 별도 Map 생성  (0) 2022.08.05
[logback] logback.xml  (0) 2022.06.21
[Jvm] JAVA_OPTS 설정  (0) 2022.06.20
HashMap pulAll & merge  (0) 2022.06.15