대부분 프로그래밍 언어에는 함수 타입이라는 개념이 없다.
그래서 연산 또는 액션을 전달할 때 메소드가 하나만 있는 인터페이스를 활용하며 이를 SAM(Single-Abstract Method)라고 부른다.
함수가 SAM을 받는다고 하면 이러한 인터페이스를 구현한 객체를 전달받는다는 의미이다.
fun setOnClickListener(listener: OnClick) {
// ...
}
setOnClickListener(object : OnClick {
override fun clicked(view: View) {
// ...
}
})
위와 같은 코드를 함수 타입을 사용하는 코드로 변경하면 더 많은 자유를 얻을 수 있다.
fun setOnClickListener(listener: (View) -> Unit) {
// ...
}
예를 들어 다음과 같은 방법으로 파라미터를 전달할 수 있다.
-
람다 표현식 또는 익명 함수로 전달
setOnClickListener { /*...*/ } setOnClickListener(fun(view) { /*...*/ })
-
함수 레퍼런스 또는 제한된 함수 레퍼런스로 전달
setOnClickListener(::println) setOnClickListener(this::showUsers)
-
선언된 함수 타입을 구현한 객체로 전달
class ClickListener: (View) -> Unit { override fun invoke(view: View) { // .. } } setOnClickListener(ClickListener())
SAM의 장점이 아규먼트에 이름이 붙어있다고 말하는 경우도 있지만, type alias를 이용해 함수 타입도 이름을 붙여 사용할 수 있다.
typealias Onclick = (View) -> Unit
또 파라미터에 이름을 가질 수 있으며 이는 IDE의 지원을 받을 수 있다는 이점이 있다.
typealias OnClick = (view: View) -> Unit
람다 표현식을 사용할 때는 아규먼트 분해(destructure argument)도 사용할 수 있다.
이것도 SAM보다 함수 타입을 사용하는게 훨씬 더 좋은 이유이다.
언제 SAM을 사용해야 할까?
코틀린이 아닌 다른 언어에서 사용할 클래스를 설계하는 경우라면 SAM을 사용하는것이 좋다.
함수 타입을 다른 언어에서 사용하는 경우 아래와 같은 단점이 있다.
- 함수 타입으로 만들어진 클래스는 자바에서 type alias과 IDE의 지원을 제대로 받을 수 없다.
-
다른 언어(자바 등)에서 코틀린의 함수 타입을 사용하려면 Unit을 명시적으로 리턴하는 함수가 필요하다.
// Kotlin class CalendarView() { var onDateclicked: ((date: Date) -> Unit)? = null var onPageChanged: OnDateClicked? = null } // Java interface OnDateClicked { fun onClick(date: Date) }
위와 같이 정의된 코틀린 클래스를 아래처럼 자바에서 사용하는 경우 Unit을 명시적으로 리턴하는 함수가 필요하다.
CalendarView c = new CalendarView(); c.setOnDateClicked(date -> Unit.INSTANCE); c.setOnPageChanged(date -> {});
자바에서 사용하기 위한 API를 설계한다면 함수 타입보다 SAM을 사용하는 것이 합리적이다. 이외의 경우라면 함수 타입을 사용하는것이 좋다.
Reference
- 이펙티브 코틀린 - 프로그래밍 인사이트, 마르친 모스칼라 지음, 윤인성 옮김
개인적인 기록을 위해 작성된 글이라 잘못된 내용이 있을 수 있습니다.
오류가 있다면 댓글을 남겨주세요.