대부분 프로그래밍 언어에는 함수 타입이라는 개념이 없다. 그래서 연산 또는 액션을 전달할 때 메서드가 하나만 있는 인터페이스를 활용하는데, 이를 SAM(Single-Abstract Method)
라고 한다.
https://kotlinlang.org/docs/fun-interfaces.html
interface OnClick {
fun clicked(view: View)
}
함수가 SAM을 받는다면, 이러한 인터페이스를 구현한 객체를 전달받는다는 의미
fun setOnClickListener(listener: OnClick) {
// ...
}
// 익명 함수?
setOnClickListener(object: OnClick {
override fun clicked(view: View) {
// ...
}
})
// 함수 타입 사용하는 코드로 변경
fun setOnClickListener(listener: (View) -> Unit) {
// ...
}
람다 표현식을 사용할 때 아규먼트 분해(destructure argument)도 사용할 수 있다.
여러 옵저버를 설정할 때, 이 장점을 확인할 수 있는데 고전적인 자바는 다음과 같이 인터페이스 기반 구현
class CalendarView {
var listener: Listener? = null
interface Listener {
fun onDateClicked(date: Date)
fun onPageChanged(date: Date)
}
}
필자는 이를 게으름의 결과라고 생각한다. API를 소비하는 사용자의 관점에서는 함수 타입을 따로따로 갖는 것이 훨씬 사용하기 쉽다고 한다.
class CalendarView {
var onDateClicked: ((date: Date) -> Unit)? = null
var onPageChanged: ((date: Date) -> Unit)? = null
}
위처럼 따로 설정하면, 각각의 것을 독립적으로 변경할 수 있다는 장점이 생긴다.
// SAM(=함수형 인터페이스)
fun interface OnClick {
fun click(count: Int)
}
// 함수 타입
fun click(count: (Int) -> Unit) {
// ...
}
fun main() {
val listener = object : OnClick {
override fun click(count: Int) {
println("클릭 횟수 $count")
}
}
listener.click(3) // 출력: 익명 클래스: 클릭 횟수 3
val listener2 = OnClick { println("클릭 횟수 $it") }
listener2.click(3)
}