Closure(클로저)
- 정의
람다를 실행 시점에 표현하는 데이터 구조는 람다에서 시작하는 모든 참조가 포함된 닫힌(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);
});