대부분 프로그래밍 언어에는 함수 타입이라는 개념이 없다. 그래서 연산 또는 액션을 전달할 때 메서드가 하나만 있는 인터페이스를 활용하는데, 이를 SAM(Single-Abstract Method)라고 한다.

image.png

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)
}