최효식 2024. 7. 20. 01:22
  • 정의

람다를 실행 시점에 표현하는 데이터 구조는 람다에서 시작하는 모든 참조가 포함된 닫힌(closed) 객체 그래프를 람다 코드와 함께 저장해야 한다.
그런 데이터 구조를 클로저(closure)라고 부른다.
(출처: Kotlin in Action)

 

정의를 보고 무슨말인가 싶은데 아래 코드를 보자.

fun View.throttleClick(
    interval: Long = 1500L,
    onClick: () -> Unit
) {
    var lastClickTime = 0L // 로컬 변수

    this.setOnClickListener {
        val currentTime = SystemClock.elapsedRealtime()
        if (currentTime - lastClickTime >= interval) {
            lastClickTime = currentTime
            onClick()
        }
    }
}

 

View 에서 따닥 방지를 위한 throttleClick 을 확장 함수로 구현한 코드이다.

여기서 lastClickTime 변수는 매번 함수가 실행 될 때 마다 0L 로 초기화가 될 것이다.

 

하지만, setOnClickListener 내부의 람다 함수에서는 이전의 lastClickTime = currentTime 으로 설정한 값이 저장되어 0L 이 아닌 저장된 값으로 if 문에서 비교를 계속 하게 된다.

 

결론 적으로  함수가 선언될 당시의 외부 환경을 Capture 하는 개념이 Closure 다! 

외부 환경이라면 setOnClickListener 입장에서는 throttleClick 함수 내부가 될 것이다.즉, 외부의 자유 변수 lastClickTime 에 대해서도 Capture 가 된다는 것이다. 그렇기 때문에 lastClickTime 에 currentTime 을 설정하고 나서 lastClickTime 을 Capture 하고 있게 되는 것이다.

 

Java 에서는 조금 다른데 Java에서는 람다 함수 내부에서 사용되는 로컬 변수가 final 또는 사실상 final(effectively final)이어야 한다. 이 제한 때문에 람다 내부에서 로컬 변수의 값을 직접 수정할 수는 없다. 

 

int x = 10;
Button button = new Button();
button.setOnClickListener(v -> {
    // x++; // 컴파일 오류: 로컬 변수는 final이어야 합니다.
    System.out.println(x);
});

 

참조