[내 맘대로 정리한 Kotlin] Functional Interaface: 오브젝트를 람다식으로 생성하기
View.setOnClickListener
안드로이드의 View.setOnClickListener는 아래처럼 OnClickListener라는 인터페이스를 받도록 설계되어 있다.
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
이 구조에 정확하게 맞추어서 코틀린 코드를 작성한다면 이렇게 될 것이다.
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
println("Clicked!")
}
})
하지만 코틀린에서는 setOnClickListener를 이렇게 복잡하게 사용하지 않는다.
onClick 메소드의 내용만이 포함된 아래 람다식 표현이 더 익숙하다.
button.setOnClickListener {
println("Clicked!")
}
이 간결한 표현식을 가능하게 해 주는 것이 바로 Functional(SAM) Interface covnersion이다.
Functional(SAM) Interface
SAM(Single Abstract Method)은 말 그대로 단 1개의 Abstract Method를 가졌다는 뜻이다.
앞서 언급한 View.setOnClickListener의 파라미터인 OnClickListener는 이렇게 생겼다.
public interface OnClickListener {
void onClick(View v);
}
단 하나의 Abstract Method를 가진(SAM) 인터페이스임을 알 수 있다.
Functional(SAM) Interface covnersion
Kotlin은 이렇게 1개의 Abstract Method를 가진 자바 인터페이스를 간결한 람다식 형태로 생성할 수 있도록 지원한다.
그렇기 때문에 우리가 setOnClickListener { println("Clicked!") } 와 같이 람다식으로 줄여 표현해도 코틀린이 이를 잘 해석해서, 'OnClickListener의 단일 메소드인 onClick의 내용을 뜻하는구나!'라고 찰떡같이 알아듣고 컴파일해 주는 것이다.
하지만 인터페이스에 대한 SAM conversion에는 조건이 있었다.
람다식으로 표현될 인터페이스와 그 메소드가 Java로 작성되어 있어야 한다는 것이다.
따라서 우리가 코틀린으로 아래처럼 Calculator 인터페이스를 작성한다고 해도...
interface Calculator {
fun calculate(a:Int, b:Int): Int
}
사용할 때에는 이를 람다식으로 표현할 수 없어 이렇게 정석적인 구현을 해야만 한다.
val adder = object: Calculator {
override fun calculate(a: Int, b: Int): Int {
return a + b
}
}
Functional(SAM) Interface in Kotlin
하지만 Kotlin 1.4부터는 코틀린으로 인터페이스를 작성하면서도 SAM conversion을 동작시키는 방법이 제공된다.
Functional Interface라는 새로운 형식의 Interface를 사용하는 것이다.
사용법은 간단하다. Interface 앞에 fun을 붙이면 된다.
* 단, SAM conversion을 위해 제공되는 Interface이므로 꼭 한 개의 abstract 메소드가 포함되어야 한다.
fun interface Calculator {
fun calculate(a:Int, b:Int): Int
}
이제 위의 adder를 이전보다 훨씬 간결하게 생성할 수 있다.
val adder = Calculator { a, b -> a+b }