함수 본문과 람다를 호출 지점에 그대로 펼쳐 넣는 것.
inline fun <T> measure(name: String, block: () -> T): T {
val start = System.nanoTime()
try { return block() }
finally { Log.d("perf", "$name: ${System.nanoTime() - start}ns") }
}
measure("load") { repository.load() }
// 컴파일 후: 이 자리에 measure 의 본문과 람다가 통째로 펼쳐진다
// → 람다 객체가 생기지 않는다
무엇을 얻나
- ① 람다 객체 생성 비용이 사라진다
- ② 비지역 반환이 가능해진다 — 람다 안에서 바깥 함수를 return 할 수 있다
- ③ reified 를 쓸 수 있다
fun find(users: List<User>): User? {
users.forEach { if (it.isAdmin) return it } // inline 이라 바깥 함수를 반환한다
return null
}
표준 라이브러리의 forEach·let·apply가 전부 inline 이라 이런 코드가 자연스럽게 동작한다.
reified — 런타임에 타입을 안다
// ❌ 일반 제네릭 — 타입 소거로 T 를 알 수 없다
fun <T> parse(json: String): T = gson.fromJson(json, T::class.java) // 컴파일 에러
// ✅ inline + reified — 호출 지점에 실제 타입이 박힌다
inline fun <reified T> parse(json: String): T = gson.fromJson(json, T::class.java)
val user: User = parse(jsonString)
// 안드로이드에서 자주 쓰는 형태
inline fun <reified T : Activity> Context.start() =
startActivity(Intent(this, T::class.java))
context.start<DetailActivity>()
제어자
inline fun f(
block: () -> Unit,
noinline callback: () -> Unit, // 이 람다는 인라인하지 않는다 (변수에 저장·반환할 때)
crossinline handler: () -> Unit // 인라인하되 비지역 반환은 금지 (다른 컨텍스트에서 실행될 때)
) { ... }
// crossinline 이 필요한 전형적 상황
inline fun runLater(crossinline block: () -> Unit) {
Handler(Looper.getMainLooper()).post { block() } // 나중에 다른 스택에서 실행된다
}
남용하면 오히려 손해
인라인은 코드를 '복사' 한다
- 함수가 크고 여러 곳에서 부르면 바이트코드가 급격히 커진다
- APK 크기 · 명령어 캐시 효율에 나쁘다
원칙: 람다를 인자로 받는 '작은' 함수에만 붙인다
- 람다가 없는 함수에 inline 을 붙이면 컴파일러가 경고한다
면접 함정
- ❌ "inline을 붙이면 빨라진다" → 람다 객체가 없어지는 것이지 로직이 빨라지지 않는다. 큰 함수면 오히려 손해다.
- ❌ "reified만 붙이면 된다" →
inline이 함께 있어야 한다.
어디에 붙어 있나 — 표준 라이브러리
// 스코프 함수 · 컬렉션 연산 대부분이 inline 이다
public inline fun <T, R> T.let(block: (T) -> R): R
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>
// 그래서 이런 코드가 람다 객체를 만들지 않는다
list.filter { it > 0 }.map { it * 2 }
컴파일 결과를 확인하는 법
Android Studio → Tools → Kotlin → Show Kotlin Bytecode → Decompile
인라인된 함수는 호출 자리에 본문이 펼쳐져 있는 것을 볼 수 있다 람다가 객체로 남았는지, 사라졌는지도 확인된다
성능 착시 주의
-
"람다 = 객체 생성 = 느리다" 는 과장이다
-
캡처가 없는 람다는 싱글턴으로 재사용된다
-
젊은 세대 GC 는 짧게 사는 객체에 매우 싸다
-
대부분의 안드로이드 코드에서 이 비용은 측정되지 않는다
정말 중요한 곳: 프레임마다 도는 코드 · 리스트 바인딩 · 애니메이션 루프 그 외에는 가독성을 우선한다