[java] 람다와 클로저

bbidag ㅣ 2024. 1. 28. 14:36

반응형

람다

  • 익명 함수의 구현 중 하나
  • 클래스, 메소드에 종속되지 않고 이름이 따로 필요하지 않다.
  • 파라미터를 입력해 반환값을 설정하는 로직을 간단하게 넣을 수 있다.
  • 순수한 람다는 외부의 상태값을 사용하지 않고 파라미터와 람다 내부의 로직을 통해서만 리턴값을 생성한다.
  • 예시)
private Function<Integer, Integer> square = x -> x * x;

@Test
public void lambdaTest() {
	System.out.println(square.apply(3));
}

 

클로저

  • 클래스나 메소드에서 정의한 변수를 람다 내부에서 사용할 수 있는 것을 의미한다.
  • 람다에서 정의하지 않고 외부에서 정의한 변수를 자유 변수(free variable)이라고 한다.
  • 람다 내부에서 사용되는 자유 변수는 인스턴스 변수와 지역 변수를 사용할 수 있다.
  • 예시로 문장을 단어 리스트로 분석하는 클래스와 메소드를 만들어보았다.
public class Analyzer {
    private List<String> stopwords = List.of("is", "a", "test", "spam");

    public List<String> analyze(String sentence) {
        String queryStr = "";
        Function<String, List<String>> tokenize = x -> {
            queryStr = x; //컴파일 에러!
            stopwords = new ArrayList<>(); // 가능
            return Arrays.stream(x.split(" "))
                    .collect(Collectors.toList());
        };
        
        Function<List<String>, List<String>> filtering = x -> x.stream()
                .filter(word -> !stopwords.contains(word)).collect(Collectors.toList());

        return tokenize.andThen(filtering).apply(sentence);
    }
}
  • 람다에서 인스턴스 변수와 지역 변수의 차이점은 변수의 재할당이 가능한지 가능하지 않은지의 차이가 있다.
  • 위의 지역 변수 queryStr를 람다 내부에서 할당하려고 하면 IDE에서 컴파일 에러 문구를 표시한다.
    • 변수는 람다 표현식 내에서 final로 선언돼있거나 final처럼 작동해야 한다고 한다.
      • Variable used in lambda expression should be final or effectively final
  • analyzer 클래스 인스턴스 변수인 stopwords는 람다 내부에서 새로 할당하더라도 문제가 없다.

 

그렇다면 왜 인스턴스 변수는 재할당이 가능하고 지역 변수는 불가능할까?

  • 인스턴스 변수는 힙 메모리에 올라가 서로 다른 스레드에서 접근이 가능하므로 재할당 하더라도 문제가 없다.
  • 지역 변수의 경우에는 각 스레드의 영역인 스택에 저장이 되기 때문에 람다 내부에 외부의 지역 변수를 사용하게 되면 람다를 실행하는 스레드가 변경된다면 원래대로라면 해당 지역 변수는 소멸되어 사용할 수가 없다.
  • 하지만 자바에서는 편의성을 위해 외부 지역 변수를 람다 내부에서 사용하게 되면 지역 변수가 복사되어 람다 인스턴스에 담겨 외부 지역변수가 없어지더라도 사용할 수 있어서 마치 람다 내부에서 선언한 지역변수처럼 된다.
    이를 람다 캡처링이라고 한다.
  • 지역 변수의 재할당이 불가능한 이유는 A 스레드에서 생성한 지역 변수가 복사되어 람다에 담기고, 람다의 실행이 B 스레드에서 이뤄질때 A 스레드에서 해당 지역 변수가 변경이 가능하다면 B 스레드의 해당 변수 사용 시점에 그 변수가 어떤 값을 가질지 예측할 수가 없게 된다.
  • 그렇기 때문에 람다에서 사용하는 외부 지역 변수는 변경이 불가능해야 한다.
  • 실제 개발에 활용을 한다면 인스턴스 변수도 final이거나 재할당해서 사용하지 않는 것이 좋은데,
    지역 변수와 같은 이유로 side effect가 생길 수 있기 때문이다.
반응형

'개발 > java' 카테고리의 다른 글

[java] junit5  (0) 2024.06.16
[java] IntelliJ 자바 개발시 애플리케이션 성능 높이는 방법  (0) 2024.01.26