본문 바로가기

Kotlin

[Kotlin] 4.람다 - 함수형 인터페이스(SAM 인터페이스)

#코틀린과 자바의 함수형 인터페이스의 관계

결론부터 말하자면

'코틀린에서 함수형 인터페이스를 인자로 받는  Java함수를 호출 할 경우, 인터페이스 객체 대신 람다를 넘길 수 있다'

예를 들면 이렇게  button.setOnClickListener { println("객체 대신 람다를 넘기고 있음") }

 

-여기서 '함수형 인터페이스'가 'SAM 인터페이스' 이다

-Java에 정의된 함수를 Kotlin에서 사용하는 경우를 말합니다

 


#SAM 인터페이스

1) SAM이란? 

  • Single abstract method / 단일 추상 메소드 

2) SAM 인터페이스란?

  • 추상 메소드가 단 1개 있는 인터페이스
  • '함수형 인터페이스' 라고도 한다(Functional interface)

3) 대표적인 SAM 인터페이스는? 

  • View.java의 OnclickListener
public interface OnClickListener {
    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    void onClick(View v);
}

 

4) 갑자기 람다와 SAM 인터페이스라니... 무슨 상관인가?

  • Sam인터페이스를 인자로 받는  Java함수를 호출 할 경우, Sam 인터페이스 객체 대신 람다를 넘길 수 있다는 사실
  • 대표적으로 'Sam인터페이스를 인자로 받는  Java 메소드'는 View.setOnClickListener를 꼽을 수 있다
public void setOnClickListener(@Nullable OnClickListener l) {
             ...
}

 

[Java에서 기본적인 setOnClickListener]는 익명 객체를 생성해서 전달 해야 하는 것이 기본인데

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {//익명객체 생성 전달}
});

 

이 경우 Kotlin에서, Sam 인터페이스 객체(익명 객체) 대신 람다를 전달 할 수 있다는 뜻

button.setOnClickListener { println("익명객체 대신 람다를 넘김 ${it.id} ") }

 

하지만 사실 Java에서도 버전 8이상에서는 동일하게 람다를 사용할 수 있었음

button.setOnClickListener(v -> Log.i("syTest","Java 에서의 람다"));

 

즉 코틀린에서도 Sam인터페이스(함수형 인터페이스)를 인자로 취하는 자바 메소드를 호출할 때 람다를 넘길 수 있도록 해준다는 뜻으로, Java와 호환성을 지원한다는 뜻

 

어떻게 이런 동작이 가능한가?

  • 람다를 함수형 인터페이스의 인자로 전달 할 때, 내부적으로 컴파일러가, 람다에 대해 익명 객체를 생성해서 메소드에 넘김
  • 익명 객체를 직접 생성해서 넘길 땐, 해당 코드를 수행할 때마다 익명 객체가 새로 생성 되지만, 람다를 사용할 땐 내부적으로 생성된 1개의 인스턴스를 재사용 한다(람다가 변수를 포획했을 때는 매번 생성 -> 포획할 변수가 매 번 바뀔 수 있으니까)
  • 이 동작은 JAVA 메소드를 코틀린에서 호출할 때 쓰이는 방식이고, 대부분의 코틀린 함수들은 람다를 넘겼을 때 다른 방식으로 동작한다(익명 객체를 생성하거나 하진 않는다. 코틀린에는 함수타입이 따로 있기 때문)

5) SAM 생성자란?

  • 컴파일러가 자동으로 람다를 함수형 인터페이스 무명 클래스로 바꾸지 못하는 경우 사용한다
  • JAVA의 함수형 인터페이스의 인스턴를 반환 하고 싶을 경우 사용한다 
  • 컴파일러가 자동 생성 해 준다
  • 간편한 코드로 인스턴스를 만들어 사용할 수 있다 

SAM 생성자를 이용해 인스턴스를 생성한 예제

람다 내용을 인터페이스의 단일 추상 메소드 본문으로 사용한다

단, 'this' 키워드로 인스턴스 자신을 참조할 수 없다

    val onClickListInstance = View.OnClickListener { println("onClick") }

 

SAM 생성자를 이용해 인스턴스를 생성한 예제 (인터페이스 직접 생성 해 봄)

자바에서 인터페이스를 정의 하고, 코틀린에서 SAM 생성자를 사용한 것을 볼 수 있다

public interface  TestInterface{
    void function_01();
}
val testInstance = TestInterface { println("Test") }

 

만약 인터페이스에 function_02()를 추가한다면 아래와 같이 컴파일 에러가 뜬다

에러 내용 : 'Interface TestInterface does not have constructors'

함수형 인터페이스, 즉 SAM 인터페이스에만 SAM 생성자를 지원하는 것을 볼 수 있다

 

SAM 인터페이스 가 아닐 경우의 인스턴스 생성 예제

  • object 키워드를 사용해서 익명 객체 생성으로 사용해야 한다
  • Kotlin에서 정의된 인터페이스의 경우엔, SAM 이더라도 object 키워드를 사용해야 한다(Test 해 봄...)
  • 'this' 키워드로 인스턴스 자신을 참조할 수 있으므로, 자기 자신이 Listener이고 Listener를 해제해야 하는 경우가 있다면 object 키워드를 사용하면 되겠다
val testInterface: TestInterface = object : TestInterface {
    override fun function_01() { println("function01") }
    override fun function_02() { println("function02") }
}

 

SAM 생성자 정리하자면

//이렇게 쓸 것을
val obejctInstance = object : View.OnClickListener{
    override fun onClick(v: View?) {
        println("onClick")
    }
}

//SAM 생성자로 간편하게 사용할 수 있다
val lambdaInstance = View.OnClickListener { println("onClick") }

 


 

잘못된 내용이나 궁금한 내용 있으시면 댓글 달아주세요

좋은 하루 되세요!

 

출처 : Kotlin In Action - 에이콘 출판사

(위 도서를 학습하고 개인 학습용으로 정리한 내용입니다)

 

 

 

 

 

 

 

 

'Kotlin' 카테고리의 다른 글

[Kotlin] CoRoutines  (0) 2021.12.07
[Kotlin] 4. 람다 - with/apply  (1) 2021.12.05
[Kotlin] 4.람다 In Collection(Sequence)  (0) 2021.11.17
[Kotlin] 4.람다 In Collection  (0) 2021.11.16
[Kotlin] 4.람다 - 기본  (0) 2021.11.15