깨알 개념/Kotlin

[Kotlin] 함수형 인터페이스 (Single Abstract Method Interface)

interfacer_han 2024. 1. 30. 13:14

#1 일반 인터페이스

#1-1 평범한 인터페이스

interface MyNormalInterface {
    fun myFirstMethod(value: Int): String
    fun mySecondMethod(value1: Int, value2: String): Int
    
    ...
}

우리가 잘 알고있는 인터페이스의 모습이다.

 

#1-2 함수형 인터페이스의 후보

// 함수형 인터페이스의 형태지만, 함수형 인터페이스는 아님
interface MyFunctionalInterfaceCandidate {
    fun myMethod(value1: Int, value2: Int): Int
}

가진 메소드가 추상 메소드 하나뿐인 인터페이스(Single Abstract Method, SAM)를 특별히 함수형 인터페이스라고 부른다. 해당 인터페이스에서 사용할 함수가 가지고 있는 하나의 함수로 밖에 결정되지 않으니, MyFunctionalInterface 자체를 myMethod()라고 생각한다. 그래서 '함수형 인터페이스'라는 이름이 붙었다.

 

하지만, 이 논리라면 가지고 있는 하나뿐인 메소드가 추상 메소드가 아니어도 '함수형 인터페이스'라고 부를 수도 있는 것 아니냐는 의문이 생긴다. 하지만, 그 경우엔 그냥 그 함수를 바로 호출하면 그만이다. 따라서 굳이 함수형 인터페이스라는 개념을 만들 필요조차 없다. 결론적으로, 함수형 인터페이스는 구현 가능한 함수가 하나 뿐인 인터페이스를 가리키는 용어다.

 

허나 위 코드는 함수형 인터페이스가 아니다. 함수형 인터페이스가 될 수 있는 후보일 뿐이다. 코틀린에서 함수형 인터페이스를 만드려면 SAM Conversions를 해야 한다. 아래 코드를 보자.

 

#2 함수형 인터페이스 (Functional Interface, Single Abstract Method Interface, SAM)

fun interface MyFunctionalInterface {
    fun myMethod(value1: Int, value2: Int): Int
}

interface 키워드 앞에 fun을 붙였다. 이는 코틀린 컴파일러에게 이 인터페이스가 함수형 인터페이스임을 명시적으로 알리는 행동이다. 이 간단한 행동을 SAM Conversions라고 한다. 말 그대로 함수형(fun) 인터페이스(interface)가 된 것이다. 이제부터 비로소 Kotlin은 해당 인터페이스를 함수형 인터페이스로 인식한다. (당연하겠지만, fun interface에 추상 메소드를 2개 이상 넣으면 에러가 난다.)

 

Java에선 함수형 인터페이스를 컴파일러가 자동으로 인식했다. 즉, 코틀린에서처럼 fun 키워드를 붙이거나 하는 작업이 없었다. 반면, Java를 계승한 Kotlin에선 일반 인터페이스와 함수형 인터페이스를 명시적으로 구분짓는다. 그 구분을 통해 프로그래머에게 명시성안전성을 제공하기 위함이다.

 

#3 익명 클래스의 인스턴스 만들기

#3-1 일반 인터페이스

interface MyNormalInterface {
    fun myFirstMethod(value: Int): String
    fun mySecondMethod(value1: Int, value2: String): Int
}

fun main() {
    val instance = object : MyNormalInterface {
        override fun myFirstMethod(value: Int): String {
            return "아무말"
        }

        override fun mySecondMethod(value1: Int, value2: String): Int {
            println("value2는 $value2")
            return value1 + 1
        }
    }
}

상용구 코드(Boilerplate code)가 많아 복잡해 보인다.

 

#3-2 함수형 인터페이스 (Functional Interface, Single Abstract Method Interface)

fun interface MyFunctionalInterface {
    fun myMethod(value1: Int, value2: Int): Int
}

fun main() {
    val instance = object : MyFunctionalInterface {
        override fun myMethod(value1: Int, value2: Int): Int {
            return value1 * value2 + 1
        }
    }
}

이처럼 함수형 인터페이스 또한 일반 인터페이스에서와 같이 상용구 코드로 범벅(?)된 코드를 짤 수 있다. 하지만, 함수형 인터페이스는 선택지가 하나 더 있다.

 

fun interface MyFunctionalInterface {
    fun myMethod(value1: Int, value2: Int): Int
}

fun main() {
    val instance = MyFunctionalInterface { value1, value2 -> value1 * value2 + 1 }
}

함수형 인터페이스 이름 + 람다 표현식 조합이면 바로 익명 클래스 생성과 인스턴스화가 동시에, 간편하게 이뤄진다. 어차피 해당 인터페이스가 가진 구현 가능한 메소드는 단 한 개뿐이라는 분명한 사실이 있기에 가능한 과감한 문법이다.

 

#4 요약

함수형 인터페이스가 단 한 개의 메소드를 가진다는 사실을 이용해, 상용구 코드를 제거한다.