안드로이드 코틀린 언어 학습 노트 목차

스코프 함수·위임 — let·run·with·apply·also·by·lazy

코틀린 코드, 특히 안드로이드 코드를 읽으면 apply { }·?.let { }·by lazy { }끝없이 나온다. 스코프 함수(let·run·with·apply·also)는 객체를 임시 스코프 안에서 다루게코드를 묶고 간결하게 만들고, **위임(by)**은 프로퍼티·구현을 다른 객체에 맡겨 보일러플레이트를 없앤다. 이 둘은 문법은 작지만 사용 빈도가 가장 높은 도구다. (출처: Kotlin Docs — Scope Functions / Delegation / Effective Kotlin Item 48·49.)

스코프 함수 5형제 — 두 축으로 구분한다

다섯이 헷갈리는 이유는 비슷해서다. 두 가지 축만 잡으면 정리된다.

  1. 컨텍스트 객체를 무엇으로 참조하나this(run·with·apply) vs it(let·also)
  2. 무엇을 반환하나람다 결과(let·run·with) vs 객체 자신(apply·also)
함수참조반환전형적 용도
letit람다 결과null 처리·변환 (?.let)
runthis람다 결과객체 설정 후 값 계산
withthis람다 결과한 객체에 여러 호출 묶기
applythis객체 자신설정(configuration) 후 그 객체 반환
alsoit객체 자신부수효과(로깅·검증)·체이닝
다이어그램 로딩 중…

각각을 코드로 — 안드로이드 관용구

// apply — 객체를 만들고 '설정'한 뒤 그 객체를 반환 (가장 흔함)
val intent = Intent(this, DetailActivity::class.java).apply {
    putExtra("id", 42)
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
}   // intent 반환

// let — null이 아닐 때만 + 변환
val upper: String? = name?.let { it.trim().uppercase() }

// also — 부수효과(로깅·검증)를 끼우고 원본을 그대로 흘림
val user = createUser().also { Log.d("TAG", "생성: $it") }

// run — 객체 컨텍스트에서 '값을 계산'해 반환
val area = rect.run { width * height }

// with — 한 객체에 여러 작업을 묶음 (확장 아님, 인자로 받음)
with(binding) {
    title.text = "제목"
    subtitle.text = "부제"
    button.isEnabled = true
}

고르는 법 — 설정 후 객체가 필요하면 apply, null 처리·변환이면 ?.let, 로깅 같은 곁다리면 also, 값을 계산해 받으면 run, 한 객체에 여러 호출이면 with. 헷갈리면 위 표의 두 축으로 되짚는다. 남용해 중첩이 깊어지면 오히려 가독성이 나빠지니 한두 단계로.

takeIf·takeUnless — 조건부로 자신/ null

val valid = input.takeIf { it.isNotBlank() }    // 조건 맞으면 자신, 아니면 null
    ?: return                                    // → Elvis와 자연스럽게 결합

위임 — by 로 일을 떠넘긴다

**클래스 위임(by)**은 인터페이스 구현을 다른 객체에 맡긴다 — *상속 대신 합성(composition)*으로 기능을 재사용하는 코틀린의 언어 차원 지원이다.

interface Repository { fun load(): List<Member> }

class CachingRepository(
    private val delegate: Repository
) : Repository by delegate {                      // load()는 delegate에 자동 위임
    // 필요한 메서드만 오버라이드, 나머진 delegate가 처리
}

프로퍼티 위임 — lazy·observable·맵

프로퍼티 위임getter/setter 로직을 위임 객체에 맡긴다. 안드로이드에서 매일 쓰는 셋:

// lazy — 첫 접근 때 한 번만 초기화 (스레드 안전 기본), 이후 캐시
val config: Config by lazy { loadConfig() }

// observable — 값이 바뀔 때마다 콜백
var name: String by Delegates.observable("") { _, old, new ->
    Log.d("TAG", "$old$new")
}

// 안드로이드 KTX — ViewModel·arguments 위임
private val viewModel: MyViewModel by viewModels()        // Fragment KTX
private val userId: Long by lazy { requireArguments().getLong("id") }
다이어그램 로딩 중…

lazy무거운 초기화(파일·DB·계산)를 실제 쓸 때까지 미루는 데 쓴다. lateinit(02장)과 비교하면 — lazyval·자동 초기화·스레드 안전, lateinitvar·수동 초기화(안드로이드 뷰 바인딩처럼 나중에 외부가 주입).

정리 — 작지만 가장 자주 쓰는 도구

스코프 함수는 객체를 임시 스코프에서 다뤄 코드를 묶고, 위임은 프로퍼티·구현을 떠넘겨 보일러플레이트를 없앤다. 둘 다 문법은 사소하지만 안드로이드 코드 어디에나 나오니 — *두 축(this/it·반환)*과 lazy/by viewModels 관용구를 손에 익히는 게 실전의 출발점이다.

한 줄 요약 — 스코프 함수는 참조(this/it) × 반환(람다결과/객체자신) 두 축: 설정→apply, null·변환→?.let, 곁다리→also, 값계산→run, 여러호출→with. 위임 by합성으로 구현을 떠넘김, 프로퍼티 위임 by lazy(지연·캐시)·observable·by viewModels().

(출처: Kotlin Docs — Scope Functions / Delegated Properties / Effective Kotlin Item 48 / Android KTX — by viewModels.)

함수 심화 — 확장·고차·람다·inline·reified컬렉션·시퀀스 — 함수형 연산·지연 평가