함수를 인자로 받거나 함수를 반환하는 함수.
// 함수 타입: (입력) -> 출력
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) }