Kotlin - 클로저(Closure)에 대해서 간단히 알아보기

클로저(Closure)는 outer scope(상위 함수의 영역)의 변수를 접근할 수 있는 함수를 말합니다. 코틀린은 클로저를 지원하며 그렇기 때문에 익명함수는 함수 밖에서 정의된 변수에 접근할 수 있습니다.

예로, 코틀린은 클로저를 지원하기 때문에 아래와 같은 코드를 사용할 수 있습니다.

fun add(x: Int): (Int) -> Int {
    return fun(y: Int): Int {
        return x + y
    }
}

fun main(args: Array<String>) {
    val func = add(10)
    val result = func(20)
    println(result)
}

위의 코드에서 add 함수는 익명함수를 리턴하는데요. 익명함수는 변수 x에 접근할 수 없을 것 같습니다. 왜냐하면 x는 전역변수도, 익명함수의 인자도 아니고 내부에서 정의하지 않았기 때문에 컴파일 에러가 발생해야 할 것 같습니다. 하지만 실행해보면 잘 동작합니다. 그 이유는 바로 코틀린이 클로저를 지원하기 때문입니다.

다시 말씀드리면, 함수 밖에 정의된 변수들을 사용할 수 있는 함수를 클로저라고 합니다. 코틀린은 클로저를 지원하며 그렇기 때문에 위 코드에서 익명함수는 문제 없이 함수 밖에서 정의된 변수에 접근할 수 있었습니다.

클로저(Closure)의 의미

Closure라는 말은 close over에서 왔습니다.

StackOverflow의 설명을 참조하였습니다.

아래 코드에서 "The function named closure is said to “close over” the variable named x."라고 할 수 있습니다.

function add(x) {
    return function closure(y) {
        return x + y;
    };
}

함수 closure는 변수 x를 쥐고있다(뒤덮다?)로 해석이 되는 것 같은데요. 그래서 클로저는 outer scope의 변수를 참조할 수 있는 함수를 말합니다.

클로저(Closure) 예제(1)

forEach함수는 익명함수를 인자로 받습니다. 람다식으로 생성한 익명함수도 클로저 함수이며, outer scope의 변수에 접근할 수 있습니다.

var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)

위의 코드에서 forEach의 인자 익명함수는 밖에 정의된 sum에 값을 저장하였습니다.

클로저(Closure) 예제(2)

안드로이드에서 자바로 아래처럼 리스너를 만들면 컴파일이 안됩니다. 왜냐하면 int x가 final이 아니기 때문입니다. 자바의 익명 객체는 outer scope의 변할 수 있는 객체를 참조할 수 없기 때문입니다.

int x = 5;

view.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        System.out.println(x);
    }
});

하지만 코틀린은 이런 코드가 가능합니다.

var x = 5

view.setOnClickListener {
  println(x)
}

x가 5일 때 View가 클릭되면 5를 출력합니다. 하지만 x가 10으로 변경되고 View를 클릭하면 10을 출력합니다.

정리

함수 밖에 정의된 변수들에 접근할 수 있는 클로저 함수에 대해서 알아보았습니다. 클로저라는 컨셉은 매우 간단하지만, 복잡한 구조를 간단히 만들 수 있기 때문에 매우 유용합니다.

참고

Loading script...
codechachaCopyright ©2019 codechacha