kyucumber
전체 글 보기

이펙티브 코틀린 아이템 38. 연산 또는 액션을 전달할 때는 인터페이스 대신 함수 타입을 사용하라

대부분 프로그래밍 언어에는 함수 타입이라는 개념이 없다.

그래서 연산 또는 액션을 전달할 때 메소드가 하나만 있는 인터페이스를 활용하며 이를 SAM(Single-Abstract Method)라고 부른다.

함수가 SAM을 받는다고 하면 이러한 인터페이스를 구현한 객체를 전달받는다는 의미이다.

fun setOnClickListener(listener: OnClick) { // ... } setOnClickListener(object : OnClick { override fun clicked(view: View) { // ... } })

위와 같은 코드를 함수 타입을 사용하는 코드로 변경하면 더 많은 자유를 얻을 수 있다.

fun setOnClickListener(listener: (View) -> Unit) { // ... }

예를 들어 다음과 같은 방법으로 파라미터를 전달할 수 있다.

  • 람다 표현식 또는 익명 함수로 전달

    setOnClickListener { /*...*/ } setOnClickListener(fun(view) { /*...*/ })
  • 함수 레퍼런스 또는 제한된 함수 레퍼런스로 전달

    setOnClickListener(::println) setOnClickListener(this::showUsers)
  • 선언된 함수 타입을 구현한 객체로 전달

    class ClickListener: (View) -> Unit { override fun invoke(view: View) { // .. } } setOnClickListener(ClickListener())

SAM의 장점이 아규먼트에 이름이 붙어있다고 말하는 경우도 있지만, type alias를 이용해 함수 타입도 이름을 붙여 사용할 수 있다.

typealias Onclick = (View) -> Unit

또 파라미터에 이름을 가질 수 있으며 이는 IDE의 지원을 받을 수 있다는 이점이 있다.

typealias OnClick = (view: View) -> Unit

람다 표현식을 사용할 때는 아규먼트 분해(destructure argument)도 사용할 수 있다.

이것도 SAM보다 함수 타입을 사용하는게 훨씬 더 좋은 이유이다.

언제 SAM을 사용해야 할까?

코틀린이 아닌 다른 언어에서 사용할 클래스를 설계하는 경우라면 SAM을 사용하는것이 좋다.

함수 타입을 다른 언어에서 사용하는 경우 아래와 같은 단점이 있다.

  • 함수 타입으로 만들어진 클래스는 자바에서 type alias과 IDE의 지원을 제대로 받을 수 없다.
  • 다른 언어(자바 등)에서 코틀린의 함수 타입을 사용하려면 Unit을 명시적으로 리턴하는 함수가 필요하다.

    // Kotlin class CalendarView() { var onDateclicked: ((date: Date) -> Unit)? = null var onPageChanged: OnDateClicked? = null } // Java interface OnDateClicked { fun onClick(date: Date) }

    위와 같이 정의된 코틀린 클래스를 아래처럼 자바에서 사용하는 경우 Unit을 명시적으로 리턴하는 함수가 필요하다.

    CalendarView c = new CalendarView(); c.setOnDateClicked(date -> Unit.INSTANCE); c.setOnPageChanged(date -> {});

자바에서 사용하기 위한 API를 설계한다면 함수 타입보다 SAM을 사용하는 것이 합리적이다. 이외의 경우라면 함수 타입을 사용하는것이 좋다.

Reference

  • 이펙티브 코틀린 - 프로그래밍 인사이트, 마르친 모스칼라 지음, 윤인성 옮김

개인적인 기록을 위해 작성된 글이라 잘못된 내용이 있을 수 있습니다.

오류가 있다면 댓글을 남겨주세요.