SAM은 Single Abstract Method의 줄임말로, 오직 하나의 추상 메소드를 가지고 있는 인터페이스를 뜻한다.
함수형 인터페이스라고도 부른다.
대표적인 예
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
OnClickListener 인터페이스는 추상메소드 onClick 하나만 가지고 있다.
대표적인 예
함수형 인터페이스를 인자로 받는 자바 함수의 대표적인 예로는 View.setOnClickListener 가 있다.
public void setOnClickListener(@Nullable OnClickListener l) {
...
}
자바 8 이전에서는 아래 코드 처럼 익명 객체를 생성해서 전달 해야 했다.
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {//익명객체 생성 전달}
});
그러나 자바 8 이후와 코틀린에서는 익명 객체를 생성하는 대신 람다를 전달 할 수 있다.
코틀린
button.setOnClickListener { println("익명객체 대신 람다를 넘김 ${it.id} ") }
자바
Java 8 이후로 사용할 수 있다.
button.setOnClickListener(v -> Log.i("syTest","Java 에서의 람다"));
어떻게 이런 동작이 가능할까?
인터페이스 객체 대신 람다를 함수형 인터페이스의 인자로 전달 할 때, 내부적으로 컴파일러가 람다에 대해 익명 객체를 생성해서 메소드에 넘긴다.
람다를 사용하지 않고 object 를 이용해 명시적으로 익명 객체를 넘길수도 있지만 이렇게 하면 해당 코드를 수행할 때마다 익명 객체가 새로 생성된다.
람다를 이용한다면 프로그램 전체에서 객체는 하나만 만들어진다. (람다가 변수를 포획했을 때는 예외적으로 매번 생성한다. 왜냐하면 포획할 변수가 매번 바뀔 수 있기 때문)
주의해야될 점은 SAM 변환은 자바에서 작성한 인터페이스일 때만 동작한다는 것이다.
코틀린에서 하나의 추상 메소드만 있는 인터페이스를 생성하고, 그 인터페이스를 인자로 받는 함수에 람다를 넘기려고 하면 오류가 발생한다.
코틀린에서 SAM 변환을 지원하지 않는 이유는 코틀린에서는 함수를 파라미터로 사용할 수 있기 때문이다.
만약 코틀린에서 SAM 변환을 사용하고 싶다면, 기존 코틀린의 인터페이스 앞에 fun 키워드를 붙여주면 된다.
그러면 해당 인터페이스는 함수형 인터페이스가 되고 SAM 변환이 동일하게 실행된다.
fun main(){
// 방법 1
doSomething{println("a")}
// 방법 2
val practice = Practice{println("a")}
doSomething(practice)
}
fun doSomething(practice: Practice){
practice.a()
}
fun interface Practice{
fun a()
}
출처