객체를 블록에 넘겨 다루는 다섯 함수. 코틀린에서 가장 자주 쓰는 도구다.
두 축으로 외운다
수신자 접근 반환값
let it 블록 결과
run this 블록 결과
with this 블록 결과 (확장 함수가 아니다)
apply this 객체 자신
also it 객체 자신
반환이 '객체 자신' → 체이닝·설정에 쓴다 (apply · also) 반환이 '블록 결과' → 변환에 쓴다 (let · run · with)
각각의 전형적 쓰임
// let — null 아닐 때만 · 변환
user?.let { showProfile(it) }
val len = text?.let { it.trim().length } ?: 0
// apply — 객체를 설정하고 그 객체를 반환
val intent = Intent(this, DetailActivity::class.java).apply {
putExtra("id", userId)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
// also — 부수 효과 (로깅 · 검증). 체인을 끊지 않는다
val result = compute().also { Log.d("TAG", "결과: $it") }
// run — 객체 컨텍스트에서 계산해 결과를 얻는다
val area = rect.run { width * height }
// with — 같은 객체에 여러 번 접근할 때 (확장이 아니라 인자로 받는다)
with(binding) {
title.text = user.name
subtitle.text = user.email
}
고르는 기준
- 객체를 설정만 하고 그대로 쓴다 — → apply
- 중간에 로그만 남기고 흐름은 유지한다 — → also
- null 검사와 함께 변환한다 — → let
- 같은 객체의 멤버를 여러 번 쓴다 — → with (또는 run)
중첩하면 읽기 어려워진다
// ❌ it 이 무엇을 가리키는지 헷갈린다
a?.let { b?.let { c?.let { ... } } }
// ✅ 이름을 붙이거나 구조를 바꾼다
if (a != null && b != null && c != null) { ... }
// 또는
val x = a ?: return
val y = b ?: return
takeIf·takeUnless
val valid = email.takeIf { it.contains("@") } // 조건에 맞으면 자신, 아니면 null
val blank = text.takeUnless { it.isBlank() }
// Elvis 와 짝을 이룬다
val name = input.takeIf { it.isNotBlank() } ?: "익명"
면접 함정
- ❌ "apply와 also는 같다" → 수신자 접근 방식이 다르다(
thisvsit). 바깥this와 이름이 겹칠 때also가 안전하다. - ❌ "스코프 함수를 쓰면 코드가 좋아진다" → 중첩되면 오히려 나빠진다. 한 겹까지가 안전선이다.
반환값을 잘못 쓰는 실수
// ❌ apply 는 객체를 반환한다 — 계산 결과를 기대하면 틀린다
val len = text.apply { length } // len 은 text 다 (String)
// ✅ 계산 결과가 필요하면 let 또는 run
val len = text.let { it.length }
val len = text.run { length }
체이닝에서의 also
fun createUser(name: String) = User(name)
.also { logger.info("생성: $it") } // 흐름을 끊지 않고 부수 효과만
.also { analytics.track("user_created") }
run의 두 얼굴
val x = obj.run { ... } // 확장 함수 — 수신자가 obj
val y = run { ... } // 최상위 함수 — 그냥 블록을 실행해 값을 얻는다
// 후자는 복잡한 초기화에 유용하다
val config = run {
val base = loadBase()
val override = loadOverride()
base + override
}
언제 안 쓰나
-
그냥 지역 변수를 쓰는 게 더 읽기 쉬울 때
-
블록이 5줄을 넘어갈 때 (함수로 뽑는다)
-
세 겹 이상 중첩될 때
-
"짧게 쓰려고" 가 아니라 "의도가 더 잘 드러나서" 쓰는 것이다