코틀린 언어 용어 사전
함수람다 · 함수 타입 · 클로저

고차 함수

함수를 인자로 받거나 반환하는 함수. 코틀린은 함수 타입이 일급이라 문법이 자연스럽다.

함수를 인자로 받거나 함수를 반환하는 함수.

// 함수 타입: (입력) -> 출력
fun repeat(n: Int, action: (Int) -> Unit) {
    for (i in 0 until n) action(i)
}

repeat(3) { println(it) }       // 마지막 인자가 람다면 괄호 밖으로 뺄 수 있다

람다 문법

val sum: (Int, Int) -> Int = { a, b -> a + b }
val square = { x: Int -> x * x }

list.filter { it > 0 }          // 파라미터가 하나면 it 으로 생략
list.map { (id, name) -> ... }  // 구조 분해
// 여러 람다를 받으면 명명 인자가 읽기 좋다
fun load(onSuccess: (Data) -> Unit, onError: (Throwable) -> Unit) { ... }

load(
    onSuccess = { render(it) },
    onError = { showError(it) }
)

클로저 — 바깥 변수를 기억한다

fun counter(): () -> Int {
    var count = 0                // 지역 변수인데
    return { ++count }           // 람다가 이걸 붙잡는다
}

val c = counter()
c(); c(); c()                    // 3

자바와 다른 점이다 — 자바 람다는 effectively final만 캡처하지만, 코틀린은 var도 캡처해 수정할 수 있다(내부적으로 래퍼 객체를 만든다).

함수 참조

list.map(::transform)            // 최상위 함수
list.map(String::uppercase)      // 멤버 함수
val f = ::User                   // 생성자

안드로이드에서

// 클릭 리스너를 람다로
button.setOnClickListener { viewModel.onClick() }

// 어댑터에 콜백을 넘긴다
class UserAdapter(private val onClick: (User) -> Unit) : RecyclerView.Adapter<...>()

성능 — 람다는 객체다

람다는 컴파일되면 객체가 된다
  → 루프 안에서 고차 함수를 자주 부르면 객체가 계속 생긴다
  → 캡처하는 변수가 없으면 인스턴스를 재사용하지만, 캡처하면 매번 생긴다

→ inline 이 이 비용을 없앤다

면접 함정

  • "람다는 무료다" → 캡처가 있으면 객체가 생긴다. 핫 패스에서는 inline을 고려한다.
  • "it은 항상 쓸 수 있다" → 파라미터가 하나일 때만이다. 중첩되면 이름을 붙이는 게 안전하다.

함수 타입의 변형

() -> Unit                    // 인자 없고 반환 없음
(Int) -> String               // Int 를 받아 String 반환
(Int, Int) -> Int
suspend () -> Unit            // 코루틴 안에서만 호출 가능
String.() -> Unit             // 수신자 있는 람다 (this 가 String)
((Int) -> Int)?               // nullable 함수 타입
// nullable 함수는 invoke 로 안전 호출한다
var onClick: (() -> Unit)? = null
onClick?.invoke()

콜백 지옥을 피하는 방향

// ❌ 콜백이 중첩된다
loadUser(id) { user ->
    loadPosts(user.id) { posts ->
        loadComments(posts[0].id) { comments -> ... }
    }
}

// ✅ suspend 로 펴진다
suspend fun load(id: Long): Result {
    val user = loadUser(id)
    val posts = loadPosts(user.id)
    return loadComments(posts[0].id)
}

콜백을 인자로 받는 API가 여럿 중첩되면 suspend로 바꿀 신호다.

인터페이스 대신 함수 타입

// 자바식 — 리스너 인터페이스를 정의한다
interface OnItemClickListener { fun onClick(item: Item) }

// 코틀린식 — 함수 타입이면 충분하다
class Adapter(private val onClick: (Item) -> Unit)
adapter = Adapter { item -> navigate(item.id) }

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 함수 심화 — 확장·고차·람다·inline·reified