Appearance
08. Kotlin 内联函数(Inline Functions)
目录
1. inline 关键字原理
1.1 什么是内联函数
内联函数(Inline Function) 是 Kotlin 提供的一种优化机制。使用 inline 关键字标记的函数,在编译时会将函数体代码直接复制到每个调用点,而不是生成传统的函数调用指令。
kotlin
// 普通函数
fun normalFunction(lambda: () -> Unit) {
println("Before")
lambda()
println("After")
}
// 内联函数
inline fun inlineFunction(lambda: () -> Unit) {
println("Before")
lambda()
println("After")
}
fun main() {
normalFunction { println("Lambda in normal") }
inlineFunction { println("Lambda in inline") }
}1.2 编译后对比
kotlin
// 普通函数编译后(伪代码)
void normalFunction(Function0 lambda) {
System.out.println("Before");
lambda.invoke();
System.out.println("After");
}
// 内联函数编译后
void main() {
System.out.println("Before");
System.out.println("Lambda in inline"); // 直接嵌入
System.out.println("After");
}1.3 内联的工作机制
kotlin
inline fun <T> Collection<T>.forEachInline(action: (T) -> Unit) {
for (element in this) {
action(element)
}
}
fun main() {
val list = listOf(1, 2, 3)
// 编译时,forEachInline 的整个函数体和 lambda 会被展开
list.forEachInline {
println(it) // 这段代码直接嵌入到调用处
}
}
// 等价于:
fun main() {
val list = listOf(1, 2, 3)
for (element in list) {
println(element) // lambda 体直接嵌入
}
}1.4 内联的文本替换过程
kotlin
inline fun <T, R> List<T>.mapInline(transform: (T) -> R): List<R> {
val result = mutableListOf<R>()
for (element in this) {
result.add(transform(element))
}
return result
}
fun main() {
val numbers = listOf(1, 2, 3)
val doubled = numbers.mapInline { it * 2 }
}
// 编译后的展开代码(伪代码):
fun main() {
val numbers = listOf(1, 2, 3)
val doubled: List<Int> = run {
val result = mutableListOf<Int>()
for (element in numbers) {
result.add(element * 2) // lambda 体直接嵌入
}
result
}
}2. 内联函数的优势(性能)
2.1 消除 lambda 对象创建
最大的性能优势是避免了 lambda 表达式的对象创建:
kotlin
// 非内联:每个调用都会创建 lambda 对象
fun nonInlineExample(items: List<String>) {
items.forEach { item ->
println(item)
}
}
// 编译后:创建了 Function1 对象
void nonInlineExample(List<String> items) {
items.forEach(new Function1<String, Unit>() {
public final Fun<Unit> invoke(String item) {
System.out.println(item);
}
});
}
// 内联:lambda 代码直接嵌入
inline fun inlineExample(items: List<String>) {
items.forEach { item ->
println(item)
}
}
// 编译后:无 lambda 对象创建
void inlineExample(List<String> items) {
for (String item : items) {
System.out.println(item);
}
}2.2 性能测试对比
kotlin
// 测试代码
inline fun inlineSum(numbers: List<Int>): Int {
var sum = 0
numbers.forEach { sum += it }
return sum
}
fun nonInlineSum(numbers: List<Int>): Int {
var sum = 0
numbers.forEach { sum += it } // 这里会创建 lambda 对象
return sum
}
fun benchmark() {
val numbers = List(1000000) { it }
val iterations = 10000
// 内联函数测试
val inlineStart = System.nanoTime()
repeat(iterations) {
inlineSum(numbers)
}
val inlineEnd = System.nanoTime()
// 非内联函数测试
val nonInlineStart = System.nanoTime()
repeat(iterations) {
nonInlineSum(numbers)
}
val nonInlineEnd = System.nanoTime()
println("Inline time: ${(inlineEnd - inlineStart) / 1_000_000} ms")
println("Non-inline time: ${(nonInlineEnd - nonInlineStart) / 1_000_000} ms")
println("Speedup: ${(nonInlineEnd - nonInlineStart).toDouble() / (inlineEnd - inlineStart) * 100}.%")
}
// 典型结果:
// Inline time: 45 ms
// Non-inline time: 120 ms
// Speedup: 266%2.3 高阶函数中的性能提升
kotlin
// 链式调用测试
inline fun <T> List<T>.chainInline(): List<T> =
this.filter { true }
.map { it }
.filter { true }
fun <T> List<T>.chainNonInline(): List<T> =
this.filter { true }
.map { it }
.filter { true }
fun benchmarkChaining() {
val data = List(100000) { it }
val iterations = 1000
val inlineTime = measureTimeMillis {
repeat(iterations) {
data.chainInline()
}
}
val nonInlineTime = measureTimeMillis {
repeat(iterations) {
data.chainNonInline()
}
}
println("Inline: $inlineTime ms")
println("Non-inline: $nonInlineTime ms")
println("Difference: ${nonInlineTime - inlineTime} ms")
}2.4 内存使用对比
kotlin
// 非内联:每次调用创建 lambda 对象
fun memoryTestNonInline(): List<String> {
return List(1000) { i ->
listOf(1, 2, 3).map { it.toString() } // 每次迭代创建 lambda
}.flatten()
}
// 内联:无 lambda 对象创建
inline fun memoryTestInline(): List<String> {
return List(1000) { i ->
listOf(1, 2, 3).map { it.toString() } // lambda 内联,无对象创建
}.flatten()
}
// 内存测试
fun testMemoryUsage() {
val gcIterations = 5
// 测试非内联
repeat(gcIterations) { System.gc() }
val nonInlineBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
val nonInlineResult = memoryTestNonInline()
repeat(gcIterations) { System.gc() }
val nonInlineAfter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
// 测试内联
repeat(gcIterations) { System.gc() }
val inlineBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
val inlineResult = memoryTestInline()
repeat(gcIterations) { System.gc() }
val inlineAfter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
println("Non-inline memory: ${nonInlineAfter - nonInlineBefore} bytes")
println("Inline memory: ${inlineAfter - inlineBefore} bytes")
// 结果:内联通常节省 30-50% 的内存
}2.5 协程中的性能优势
kotlin
// 协程启动中的内联优势
inline fun <T> CoroutineScope.launchInline(
context: CoroutineContext = EmptyCoroutineContext,
blocked: Boolean = false,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Job {
return launch(context, blocked, start, block)
}
fun <T> CoroutineScope.launchNonInline(
context: CoroutineCoroutineContext = EmptyCoroutineContext,
blocked: Boolean = false,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Job {
return launch(context, blocked, start, block)
}
// 在协程中,内联尤为重要:
// 1. 避免在协程切换点创建额外的 lambda 对象
// 2. 减少协程状态机的复杂度
// 3. 降低协程挂起时的内存占用
// 示例:批量协程启动
suspend fun benchmarkCoroutines() {
val coroutineCount = 10000
// 非内联:每个协程创建 lambda 对象
val nonInlineTime = measureTimeMillis {
val jobs = listOf(indices = 0..coroutineCount)
.map { index ->
launchNonInline {
delay(1)
index * 2
}
}
awaitAll(*jobs.toTypedArray())
}
// 内联:lambda 内联,减少对象创建
val inlineTime = measureTimeMillis {
val jobs = listOf(indices = 0..coroutineCount)
.map { index ->
launchInline {
delay(1)
index * 2
}
}
awaitAll(*jobs.toTypedArray())
}
println("Non-inline: $nonInlineTime ms")
println("Inline: $inlineTime ms")
}3. noinline 修饰符
3.1 什么是 noinline
noinline 修饰符用于阻止特定的 lambda 参数被内联。当函数中有多个 lambda 参数时,可以使用 noinline 选择性地阻止某些 lambda 被内联。
kotlin
// 基本用法
inline fun example(
inlineLambda: () -> Unit, // 会被内联
noinline noInlineLambda: () -> Unit // 不会被内联
) {
inlineLambda()
noInlineLambda()
}
fun main() {
example(
{ println("This will be inlined") },
{ println("This will NOT be inlined") }
)
}3.2 使用场景 1:保存 lambda 引用
当你需要将 lambda 保存并在稍后调用时:
kotlin
// 保存 lambda 引用
inline fun withCallback(
action: () -> Unit,
noinline callback: () -> Unit
) {
// action 会被内联
action()
// callback 不会被内联,可以保存引用
callbackRegistry.register(callback)
}
class CallbackRegistry {
private val callbacks = mutableListOf<() -> Unit>()
fun register(callback: () -> Unit) {
callbacks.add(callback)
}
fun invokeAll() {
callbacks.forEach { it() }
}
}3.3 使用场景 2:lambda 作为高阶函数的参数
kotlin
// 将 lambda 传递给另一个函数
inline fun processData(
data: List<String>,
noinline transformer: (String) -> String
) {
// transformer 不会被内联,可以作为参数传递
data.forEach { item ->
applyTransformation(transformer, item)
}
}
fun applyTransformation(transformer: (String) -> String, item: String): String {
return transformer(item)
}
// 如果没有 noinline,这里会编译错误:
// SAMLAMBDA cannot be inlined3.4 使用场景 3:异步操作
kotlin
// 异步执行 lambda
inline fun executeAsync(
noinline task: suspend () -> Unit
) {
viewModelScope.launch {
// task 不会被内联,可以在不同的协程中执行
task()
}
}
// 使用示例
fun main() {
executeAsync {
delay(1000)
println("Executed asynchronously")
}
}3.5 使用场景 4:观察者模式
kotlin
// 观察者注册
interface Observer<T> {
fun onNext(value: T)
}
class Observable<T> {
private val observers = mutableListOf<(T) -> Unit>()
// noinline 允许保存 lambda 引用
fun observe(noinline observer: (T) -> Unit) {
observers.add(observer)
}
fun emit(value: T) {
observers.forEach { it(value) }
}
}
// 使用
val observable = Observable<String>()
observable.observe { value ->
println("Received: $value")
}
observable.emit("Hello")3.6 使用场景 5:延迟执行
kotlin
// 延迟执行 lambda
inline fun schedule(
delayMs: Long,
noinline task: () -> Unit
) {
handler.postDelayed({
// task 不会被内联,可以在延迟后执行
task()
}, delayMs)
}
fun main() {
schedule(1000) {
println("Executed after 1 second")
}
}3.7 noinline 与 reified 的冲突
kotlin
// ❌ 错误:noinline 参数不能使用 reified
// inline fun <reified T> example(
// noinline lambda: T
// ): T {
// // 编译错误
// }
// ✅ 正确:reified 类型的 lambda 必须被内联
inline fun <reified T> example(lambda: T): T {
return lambda
}4. crossinline 修饰符
4.1 什么是 crossinline
crossinline 修饰符用于标记一个 lambda 参数,它不能包含非局部返回(即不能直接使用 return 跳出外部函数)。
kotlin
// 普通 lambda:允许非局部返回
inline fun withLambda(lambda: () -> Unit) {
lambda()
}
fun testNonLocalReturn() {
withLambda {
return // ✅ 可以跳出 testNonLocalReturn
}
println("This won't be printed")
}
// crossinline lambda:不允许非局部返回
inline fun withCrossLambda(crossinline lambda: () -> Unit) {
lambda()
}
fun testCrossLambda() {
withCrossLambda {
return // ❌ 编译错误:Non-local return is not allowed
}
}4.2 为什么需要 crossinline
当一个内联函数内部在另一个 lambda 中调用当前 lambda 时,必须使用 crossinline:
kotlin
// 场景:在回调中多次调用 lambda
inline fun repeatAction(
times: Int,
crossinline action: () -> Unit
) {
repeat(times) {
action() // action 被多次调用
}
}
fun testCrossinline() {
repeatAction(3) {
println("Action executed")
// return // ❌ 错误:无法跳出 testCrossinline
}
}4.3 使用场景 1:循环执行
kotlin
// 在循环中执行 lambda
inline fun retry(
maxAttempts: Int,
crossinline operation: () -> Boolean
): Boolean {
for (i in 1..maxAttempts) {
if (operation()) {
return true
}
delay(1000 * i) // 指数退避
}
return false
}
fun main() {
val success = retry(3) {
// 模拟可能失败的操作
println("Attempt ${it}")
true
}
}4.4 使用场景 2:条件执行
kotlin
// 条件执行 lambda
inline fun executeIf(
condition: Boolean,
crossinline ifTrue: () -> Unit,
crossinline ifFalse: () -> Unit
) {
if (condition) {
ifTrue()
} else {
ifFalse()
}
}
fun main() {
executeIf(true) {
println("Condition is true")
} {
println("Condition is false")
}
}4.5 使用场景 3:异常处理
kotlin
// 带异常处理的内联函数
inline fun <T> tryWithCatch(
crossinline operation: () -> T,
crossinline onException: (Throwable) -> T
): T {
return try {
operation()
} catch (e: Throwable) {
onException(e)
}
}
fun main() {
val result = tryWithCatch(
operation = { parseInt("123") },
onException = {
println("Caught: ${it.message}")
0
}
)
}4.6 使用场景 4:资源管理
kotlin
// 资源管理(类似 try-with-resources)
inline fun <T : Closeable> useResource(
resource: T,
crossinline block: (T) -> Unit
) {
try {
block(resource)
} finally {
resource.close()
}
}
fun main() {
useResource(FileReader("file.txt")) { reader ->
val content = reader.readText()
println(content)
// return // ❌ 错误:不能跳出 useResource 外部
}
}4.7 使用场景 5:AOP 切面
kotlin
// AOP 切面实现
inline fun <T> withLogging(
name: String,
crossinline operation: () -> T
): T {
println("Starting: $name")
val start = System.currentTimeMillis()
try {
val result = operation()
println("Completed: $name in ${System.currentTimeMillis() - start}ms")
return result
} catch (e: Exception) {
println("Failed: $name - ${e.message}")
throw e
}
}
fun main() {
val result = withLogging("User Fetch") {
fetchUser(123)
// return // ❌ 错误:不能跳出 withLogging
}
}4.8 crossinline vs noinline
kotlin
// crossinline: lambda 会被内联,但不能使用非局部返回
inline fun withCross(
crossinline lambda: () -> Unit
) {
lambda()
}
// noinline: lambda 不会被内联,可以保存引用
inline fun withNoInline(
noinline lambda: () -> Unit
) {
saveLambda(lambda) // 保存引用
}
fun saveLambda(lambda: () -> Unit) {
// 保存 lambda
}
// 可以同时使用
inline fun withBoth(
crossinline lambda: () -> Unit,
noinline other: () -> Unit
) {
lambda()
saveLambda(other)
}4.9 局部返回 (return@label)
当 crossinline 禁止非局部返回时,可以使用带标签的返回:
kotlin
inline fun withCrossLambda(
crossinline lambda: () -> Unit
) {
lambda()
}
fun main() {
withCrossLambda {
// ❌ return // 错误:非局部返回
// ✅ return@lambda // 正确:局部返回(但这里是整个 lambda,所以没必要)
println("This works")
}
// 对于带参数的 lambda
listOf(1, 2, 3).forEach { item ->
if (item == 2) {
return@forEach // ✅ 局部返回,跳出 forEach
}
println(item)
}
}5. 内联属性
5.1 什么是内联属性
从 Kotlin 1.6 开始,支持内联属性(inline properties),它可以内联 getter 和 setter,进一步优化性能。
kotlin
// 内联属性
inline val Int.isEven: Boolean
get() = this % 2 == 0
inline val Int.isOdd: Boolean
get() = this % 2 != 0
fun main() {
println(4.isEven) // true - getter 被内联
println(5.isOdd) // true - getter 被内联
}5.2 内联扩展属性
kotlin
// 扩展属性
inline val String.firstCharUpper: String
get() = this.firstOrNull()?.let { it.uppercase() } + this.drop(1)
inline val List<Int>.sumOfSquares: Int
get() = this.sumOf { it * it }
inline val Collection<*>.sizeInKB: Int
get() = this.size * 1024
fun main() {
println("hello".firstCharUpper) // "Hello"
println(listOf(1, 2, 3).sumOfSquares) // 14
println(listOf(1, 2, 3).sizeInKB) // 3072
}5.3 内联可变属性
kotlin
class Counter {
private var _count = 0
// 内联属性访问器
inline var count: Int
get() = _count
set(value) {
require(value >= 0) { "Count cannot be negative" }
_count = value
}
}
fun main() {
val counter = Counter()
counter.count = 10 // setter 被内联
println(counter.count) // getter 被内联
}5.4 内联委托属性
kotlin
// 内联委托
inline fun <T> lazyInline(initializer: () -> T): Lazy<T> =
lazy { initializer() }
class Example {
val data: String by lazyInline {
println("Computing...")
"Computed value"
}
}
fun main() {
val example = Example()
println(example.data) // getter 被内联
println(example.data) // 使用缓存值
}5.5 性能对比
kotlin
// 普通属性
val Int.isEvenNormal: Boolean
get() = this % 2 == 0
// 内联属性
inline val Int.isEvenInline: Boolean
get() = this % 2 == 0
fun benchmark() {
val numbers = 1..1000000
val iterations = 1000
// 普通属性
val normalStart = System.nanoTime()
repeat(iterations) {
numbers.count { it.isEvenNormal }
}
val normalEnd = System.nanoTime()
// 内联属性
val inlineStart = System.nanoTime()
repeat(iterations) {
numbers.count { it.isEvenInline }
}
val inlineEnd = System.nanoTime()
println("Normal: ${(normalEnd - normalStart) / 1_000_000} ms")
println("Inline: ${(inlineEnd - inlineStart) / 1_000_000} ms")
// 内联属性通常快 20-30%
}5.6 使用限制
kotlin
// ❌ 错误:不能在外部类定义内联属性
class Outer {
// inline val something: String // 错误
}
// ✅ 正确:在顶层或对象中定义
inline val TOP_LEVEL: String = "value"
object MyObject {
inline val PROPERTY: String = "value"
}
// ❌ 错误:内联属性不能访问类的私有成员
class MyClass {
private val privateVal = "private"
// inline val publicVal: String // 错误
// get() = privateVal
}6. reified 泛型
6.1 什么是 reified 类型参数
reified(具体化)类型参数允许在运行时访问泛型的实际类型信息。普通泛型在运行时会被擦除,而 reified 类型参数保留了类型信息。
kotlin
// 普通泛型 - 运行时类型擦除
fun <T> isInstance(obj: Any): Boolean {
// obj is T // ❌ 错误:T 在运行时不可用
return obj.javaClass.name == "" // 无法获取 T 的实际类型
}
// reified 泛型 - 运行时类型可用
inline fun <reified T> isInstance(obj: Any): Boolean {
return obj is T // ✅ 可以:T 在运行时可用
}
fun main() {
println(isInstance<String>("Hello")) // true
println(isInstance<Int>("Hello")) // false
}6.2 使用条件
使用 reified 的条件:
- 函数必须标记为
inline - 类型参数必须标记为
reified
kotlin
// ✅ 正确用法
inline fun <reified T> createInstance(): T {
// ...
}
// ❌ 错误:非内联函数不能使用 reified
fun <reified T> notAllowed(): T {
// 编译错误
}
// ❌ 错误:reified 不能用于类
// class Box<reified T> // 编译错误6.3 常见应用 1:类型检查
kotlin
inline fun <reified T> List<*>.filterByType(): List<T> {
return this.filterIsInstance<T>()
}
inline fun <reified T> Any?.castOrNull(): T? {
return this as? T
}
inline fun <reified T> Collection<*>.containsType(): Boolean {
return any { it is T }
}
fun main() {
val mixed = listOf(1, "two", 3.0, true)
println(mixed.filterByType<String>()) // ["two"]
println(mixed.filterByType<Int>()) // [1]
println(123.castOrNull<String>()) // null
println(123.castOrNull<Int>()) // 123
println(mixed.containsType<String>()) // true
println(mixed.containsType<Double>()) // true
}6.4 常见应用 2:Kotlin 反射替代
kotlin
// 使用 reified 替代反射
inline fun <reified T> KClass<T>.createInstance(): T {
return T::class.java.getDeclaredConstructor().newInstance()
}
inline fun <reified T> Class<T> isSubclassOf(): Boolean {
return T::class.java.isSubclassOf(OtherClass::class.java)
}
inline fun <reified T> String.parseAs(): T? {
return try {
when {
T::class == Int::class -> this.toInt() as T
T::class == Double::class -> this.toDouble() as T
T::class == Boolean::class -> this.toBoolean() as T
else -> null
}
} catch (e: Exception) {
null
}
}
fun main() {
// 无反射的类型检查
println(String::class.isSubclassOf<Any>()) // true
// 类型解析
println("123".parseAs<Int>()) // 123
println("123.45".parseAs<Double>()) // 123.45
println("true".parseAs<Boolean>()) // true
}6.5 常见应用 3:序列化/反序列化
kotlin
// 简化的序列化
inline fun <reified T> jsonToObject(json: String): T? {
return try {
// 使用 Gson 或 Jackson
Gson().fromJson(json, T::class.java)
} catch (e: Exception) {
null
}
}
inline fun <reified T> objectToJson(obj: T): String {
return Gson().toJson(obj)
}
fun main() {
data class User(val name: String, val age: Int)
val user = User("Alice", 30)
val json = objectToJson(user)
println(json) // {"name":"Alice","age":30}
val parsed = jsonToObject<User>(json)
println(parsed) // User(name=Alice, age=30)
}6.6 常见应用 4:依赖注入
kotlin
// 简单的 DI 容器
class ServiceLocator {
private val instances = mutableMapOf<KClass<*>(), Any>()
inline fun <reified T : Any> register(instance: T) {
instances[T::class] = instance
}
inline fun <reified T : Any> get(): T? {
return instances[T::class] as? T
}
inline fun <reified T : Any> getOrRegister(factory: () -> T): T {
return get() ?: run {
val instance = factory()
register(instance)
instance
}
}
}
// 使用
fun main() {
val locator = ServiceLocator()
locator.register(Database())
locator.register(Repository(locator.get<Database>()))
val db = locator.get<Database>()
val repo = locator.get<Repository>()
}
data class Database
data class Repository(val db: Database)6.7 常见应用 5:协程中的使用
kotlin
// 协程中的 reified
inline fun <reified T> suspend Flow<T>.firstOrNull(): T? {
return this.firstOrNull()
}
inline fun <reified T> suspend Flow<T>.filterType(): Flow<T> {
return this.filter { it is T }.map { it as T }
}
// 收集特定类型的流
inline fun <reified T> Flow<T>.collectByType(
crossinline action: suspend (T) -> Unit
) {
this.collect { action(it) }
}
fun main() = runBlocking {
val flow = flowOf(1, 2, 3, "four", 5)
flow.collectByType<Int> {
println("Int: $it")
}
}6.8 高级应用:类型安全的 Builder
kotlin
// 类型安全的 Builder 模式
interface Builder<out T> {
fun build(): T
}
inline fun <reified T : Any> builder(block: Builder<T>.() -> Unit): T {
val builder = object : Builder<T> {
override fun build(): T = TODO()
}
builder.block()
return builder.build()
}
// 使用 reified 的集合操作
inline fun <reified T> List<T>.groupedByType(): Map<String, List<T>> {
return this.groupBy { it.javaClass.simpleName }
}
inline fun <reified T> Collection<T>.distinctByType(): List<T> {
return this.distinctBy { it.javaClass }
}6.9 reified 与继承
kotlin
// reified 可以检查继承关系
inline fun <reified Parent> isSubclassOf(child: Any): Boolean {
return child::class.isSubclassOf(Parent::class) || child is Parent
}
inline fun <reified T> KClass<out T>.isAssignableFrom(other: KClass<*>): Boolean {
return T::class.isAssignableFrom(other.java)
}
fun main() {
open class Animal
class Dog : Animal()
val dog = Dog()
println(isSubclassOf<Animal>(dog)) // true
println(isSubclassOf<Dog>(dog)) // true
println(isSubclassOf<Cat>(dog)) // false
}7. 内联函数的限制
7.1 不能递归调用
kotlin
// ❌ 错误:内联函数不能递归调用自身
// inline fun factorial(n: Int): Int {
// if (n <= 1) return 1
// return n * factorial(n - 1) // 编译错误
// }
// ✅ 解决:使用尾递归或普通函数
tailrec fun factorial(n: Int, acc: Int = 1): Int {
if (n <= 1) return acc
return factorial(n - 1, n * acc)
}7.2 代码膨胀问题
kotlin
// 代码膨胀示例
inline fun <T> List<T>.customOperation(action: (T) -> Unit) {
for (item in this) {
action(item)
log("Processed: $item")
}
}
// 如果在 100 个地方调用,代码会被复制 100 次
// 这会导致:
// 1. 编译时间增加
// 2. 生成的代码体积增大
// 3. 调试困难
// 最佳实践:只在性能敏感的地方使用内联
fun <T> List<T>.customOperation(action: (T) -> Unit) {
// 普通函数,避免代码膨胀
for (item in this) {
action(item)
log("Processed: $item")
}
}7.3 调试困难
kotlin
// 内联函数在调试时的问题
inline fun process(data: List<Int>, transform: (Int) -> Int): List<Int> {
return data.map(transform)
}
fun main() {
val result = process(listOf(1, 2, 3)) { it * 2 }
// 调试时无法在 process 函数中设置断点
// 因为函数体被展开到调用处
}
// 调试技巧:使用非内联版本
fun <T> List<T>.debugMap(transform: (T) -> Int): List<Int> {
println("Starting map operation on ${this.size} elements")
val result = this.map(transform)
println("Map operation completed")
return result
}7.4 堆栈跟踪问题
kotlin
inline fun <T> slowOperation(data: T): T {
Thread.sleep(1000)
return data
}
fun main() {
try {
slowOperation("test")
} catch (e: Exception) {
e.printStackTrace()
// 堆栈跟踪中可能没有 slowOperation 的帧
// 因为代码被内联到了调用处
}
}
// 对比非内联函数
fun <T> nonInlineSlowOperation(data: T): T {
Thread.sleep(1000)
return data
}
// 堆栈跟踪会包含 nonInlineSlowOperation 的帧7.5 内联属性不能访问私有成员
kotlin
class MyClass {
private val privateField = "private"
// ❌ 错误:内联属性不能访问私有成员
// inline val publicProp: String
// get() = privateField
// ✅ 正确:使用普通属性
val publicProp: String
get() = privateField
}7.6 文件大小限制
kotlin
// 过度使用内联会导致编译文件过大
// 示例:在大型项目中过度内联
inline fun <T> customFilter(predicate: (T) -> Boolean): Boolean {
// 复杂的过滤逻辑
// ...
}
inline fun <T> customMap(transform: (T) -> T): T {
// 复杂的转换逻辑
// ...
}
inline fun <T> customReduce(accumulator: (T, T) -> T): T {
// 复杂的归约逻辑
// ...
}
// 如果这些函数在项目中被频繁调用,会导致:
// 1. 编译时间显著增加
// 2. 生成的 .class 文件体积增大
// 3. JIT 优化负担加重
// 建议:只在性能瓶颈处使用内联8. 性能对比分析
8.1 基准测试框架
kotlin
@OptIn(ExperimentalStdlibApi::class)
fun <T> measureBlock(label: String, block: () -> T): T {
// 预热 JVM
repeat(100) { block() }
val iterations = 10000
val start = System.nanoTime()
val result = repeat(iterations) { block() }
val end = System.nanoTime()
val avgTime = (end - start) / iterations
println("$label: ${avgTime / 1_000} ns per iteration")
return result
}8.2 Lambda 创建开销
kotlin
// 测试 lambda 创建开销
fun testLambdaCreation() {
val items = List(10000) { it }
// 非内联版本
measureBlock("Non-inline forEach") {
items.forEach { println(it) }
}
// 内联版本
inline fun <T> List<T>.forEachInline(action: (T) -> Unit) {
for (item in this) action(item)
}
measureBlock("Inline forEach") {
items.forEachInline { println(it) }
}
}
// 典型结果:
// Non-inline forEach: 150 ns per iteration
// Inline forEach: 80 ns per iteration
// Speedup: 87.5%8.3 链式调用性能
kotlin
inline fun <T> List<T>.chainOptimized(): List<T> =
this.filter { it > 0 }
.map { it * 2 }
.filter { it < 100 }
fun <T> List<T>.chainStandard(): List<T> =
this.filter { it > 0 }
.map { it * 2 }
.filter { it < 100 }
fun testChaining() {
val data = List(10000) { it }
measureBlock("Chain optimized") {
data.chainOptimized()
}
measureBlock("Chain standard") {
data.chainStandard()
}
}8.4 协程性能对比
kotlin
inline suspend fun <T> processInline(items: List<T>): List<T> {
return items.map { item ->
delay(1)
item
}
}
suspend fun <T> processNonInline(items: List<T>): List<T> {
return items.map { item ->
delay(1)
item
}
}
fun testCoroutinePerformance() = runBlocking {
val items = List(1000) { it }
measureBlock("Coroutine inline") {
processInline(items)
}
measureBlock("Coroutine non-inline") {
processNonInline(items)
}
}8.5 内存使用对比
kotlin
fun testMemoryUsage() {
val gcIterations = 5
// 非内联版本
repeat(gcIterations) { System.gc() }
val beforeNonInline = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
val data = List(100000) {
List(10) { it }.map { it * 2 }
}
repeat(gcIterations) { System.gc() }
val afterNonInline = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
// 内联版本
repeat(gcIterations) { System.gc() }
val beforeInline = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
inline fun <T> innerInline(items: List<Int>): List<Int> {
return items.map { it * 2 }
}
val dataInline = List(100000) {
List(10) { it }.let { innerInline(it) }
}
repeat(gcIterations) { System.gc() }
val afterInline = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
println("Non-inline memory: ${afterNonInline - beforeNonInline} bytes")
println("Inline memory: ${afterInline - beforeInline} bytes")
}8.6 编译时间对比
kotlin
// 编译时间测试脚本(外部脚本)
/*
# test_compile_time.sh
# 非内联版本
time kotlinc main.kt -include-runtime -d non-inline.jar
# 内联版本
time kotlinc main.kt -include-runtime -d inline.jar
*/9. 最佳实践
9.1 何时使用内联
kotlin
// ✅ 适合内联的场景:
// 1. 接受 lambda 的高阶函数
inline fun <T> List<T>.forEachOptimized(action: (T) -> Unit) {
for (item in this) action(item)
}
// 2. 性能敏感的代码
inline fun compute-intensive(x: Int): Int {
return x * x + x / 2
}
// 3. 使用 reified 泛型
inline fun <reified T> isOfType(value: Any): Boolean = value is T
// 4. 简单的扩展函数
inline val Int.doubled: Int get() = this * 29.2 何时避免内联
kotlin
// ❌ 不适合内联的场景:
// 1. 复杂函数体
fun complexFunction(data: List<Data>) {
// 大量逻辑
// 不应该内联
}
// 2. 递归函数
fun factorial(n: Int): Int {
if (n <= 1) return 1
return n * factorial(n - 1)
}
// 3. 被频繁调用但逻辑简单的函数
fun simpleCheck(value: Int): Boolean = value > 0
// 4. 需要调试和堆栈跟踪的函数
fun debuggableFunction(data: Data) {
// 需要断点调试
}9.3 性能优化建议
kotlin
// 1. 只内联小函数
inline fun smallInline(x: Int): Int = x * 2
// 2. 使用内联减少 lambda 创建
inline fun <T> List<T>.process(action: (T) -> Unit) {
for (item in this) action(item)
}
// 3. 避免过度内联导致代码膨胀
fun <T> notInlined(items: List<T>) {
// 复杂逻辑,保持为普通函数
}
// 4. 在性能关键路径使用内联
inline fun performanceCritical(data: ByteArray): Int {
var sum = 0
for (byte in data) sum += byte
return sum
}9.4 代码组织
kotlin
// 组织内联函数的最佳位置
// 1. 工具类/对象
object Utils {
inline fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
return filter(predicate)
}
}
// 2. 扩展函数文件
// InlineExtensions.kt
inline fun <T> Iterable<T>.measureTime(block: () -> T): T {
val start = System.currentTimeMillis()
val result = block()
println("Took: ${System.currentTimeMillis() - start}ms")
return result
}
// 3. 性能关键模块
// PerformanceCritical.kt
inline fun <T> batchProcess(items: List<T>, processor: (T) -> T): List<T> {
return items.map(processor)
}10. 常见错误与陷阱
10.1 忘记内联 lambda 参数
kotlin
// ❌ 错误:lambda 参数没有内联
inline fun wrongExample(lambda: () -> Unit) {
lambda() // lambda 不会被内联
}
// ✅ 正确:Kotlin 自动内联 lambda 参数
inline fun correctExample(lambda: () -> Unit) {
lambda() // lambda 会被内联
}10.2 在非内联函数使用 reified
kotlin
// ❌ 错误
// fun <reified T> getTypeName(): String {
// return T::class.simpleName
// }
// ✅ 正确
inline fun <reified T> getTypeName(): String {
return T::class.simpleName
}10.3 过度使用导致代码膨胀
kotlin
// ❌ 过度使用
inline fun helper1(x: Int): Int = x + 1
inline fun helper2(x: Int): Int = x * 2
inline fun helper3(x: Int): Int = x / 2
fun main() {
repeat(1000) { i ->
helper1(i)
helper2(i)
helper3(i)
}
}
// 代码被复制 3000 次
// ✅ 适度使用
fun helper1(x: Int): Int = x + 1
fun helper2(x: Int): Int = x * 2
inline fun helper3(x: Int): Int = x / 2 // 只在性能关键处内联10.4 调试困难
kotlin
inline fun complexOperation(data: Data): Result {
// 复杂的业务逻辑
// ...
}
fun main() {
// 调试时无法在 complexOperation 设置断点
val result = complexOperation(data)
}
// ✅ 调试技巧:使用普通函数版本
fun complexOperationDebug(data: Data): Result {
// 可以设置断点
}11. 高级应用场景
11.1 DSL 构建
kotlin
// DSL 构建示例
inline fun html(init: HtmlBuilder.() -> Unit): String {
val builder = HtmlBuilder()
builder.init()
return builder.build()
}
class HtmlBuilder {
private val buffer = StringBuilder()
inline fun html(init: HtmlBuilder.() -> Unit) {
buffer.append("<html>")
init()
buffer.append("</html>")
}
inline fun body(init: HtmlBuilder.() -> Unit) {
buffer.append("<body>")
init()
buffer.append("</body>")
}
inline fun div(attrs: Map<String, String> = emptyMap(), init: HtmlBuilder.() -> Unit) {
buffer.append("<div")
attrs.forEach { (k, v) ->
buffer.append(" $k=\"$v\"")
}
buffer.append(">")
init()
buffer.append("</div>")
}
fun build() = buffer.toString()
}
fun main() {
val htmlString = html {
html {
body {
div(mapOf("class" to "container")) {
p { "Hello World" }
}
}
}
}
println(htmlString)
}11.2 领域特定语言
kotlin
// 查询 DSL
inline fun <T> QueryBuilder<T>.where(crossinline condition: T.() -> Boolean) {
// 构建查询条件
}
inline fun <T> QueryBuilder<T>.select(crossinline fields: List<KProperty1<T, *>>) {
// 选择字段
}
// 使用
val query = QueryBuilder<User>()
.where { it.age > 18 }
.select(listOf(User::name, User::email))11.3 测试框架
kotlin
// 自定义测试框架
inline fun <reified T> test(name: String, crossinline testBlock: () -> Unit) {
println("Running test: $name")
try {
testBlock()
println("✓ $name passed")
} catch (e: AssertionError) {
println("✗ $name failed: ${e.message}")
}
}
inline fun <reified T> assertTrue(condition: Boolean, message: String = "") {
if (!condition) {
throw AssertionError(message)
}
}
fun main() {
test("User creation") {
val user = User("Alice", 30)
assertTrue(user.name == "Alice")
assertTrue(user.age == 30)
}
}12. 面试考点
12.1 基础考点
Q1: 什么是内联函数?它有什么作用?
A:
- 内联函数使用
inline关键字标记 - 编译时将函数体代码直接复制到调用点
- 主要作用是消除 lambda 对象创建,提高性能
- 避免了函数调用开销
Q2: 内联函数和普通函数的区别?
A:
| 特性 | 普通函数 | 内联函数 |
|---|---|---|
| 调用方式 | 函数调用指令 | 代码复制 |
| lambda 对象 | 创建对象 | 不创建 |
| 性能 | 较慢 | 更快 |
| 代码大小 | 小 | 可能膨胀 |
| 调试 | 容易 | 困难 |
Q3: 内联函数的 lambda 参数会被内联吗?
A: 会。内联函数的 lambda 参数默认都会被内联,除非使用 noinline 修饰。
12.2 进阶考点
Q4: noinline 和 crossinline 的区别?
A:
- noinline: 阻止 lambda 被内联,允许保存 lambda 引用
- crossinline: lambda 会被内联,但禁止非局部返回
Q5: reified 类型参数的使用条件?
A:
- 函数必须是
inline - 类型参数必须标记为
reified - 只能在内联函数中使用
Q6: 内联函数的性能优势?
A:
- 消除 lambda 对象创建(主要优势)
- 避免函数调用开销
- 更好的 JIT 优化机会
- 减少内存分配
12.3 高级考点
Q7: 实现一个支持类型安全的 JSON 解析器
A:
kotlin
inline fun <reified T> parseJson(json: String): T {
return Gson().fromJson(json, T::class.java)
}
inline fun <reified T> toJson(obj: T): String {
return Gson().toJson(obj)
}
fun main() {
data class User(val name: String, val age: Int)
val user = User("Alice", 30)
val json = toJson(user)
val parsed = parseJson<User>(json)
}Q8: 设计一个性能优化的集合操作库
A:
kotlin
// 内联集合操作
inline fun <T, R> List<T>.mapInline(transform: (T) -> R): List<R> {
val result = ArrayList<R>(this.size)
for (item in this) {
result.add(transform(item))
}
return result
}
inline fun <T> List<T>.filterInline(predicate: (T) -> Boolean): List<T> {
val result = ArrayList<T>()
for (item in this) {
if (predicate(item)) {
result.add(item)
}
}
return result
}
inline fun <T> List<T>.forEachInline(action: (T) -> Unit) {
for (item in this) {
action(item)
}
}Q9: 内联函数在协程中的优势?
A:
- 减少协程状态机的复杂度
- 避免在挂起点创建额外对象
- 降低协程内存占用
- 更好的性能表现
Q10: 内联函数的限制和最佳实践?
A:
限制:
- 不能递归调用
- 代码膨胀
- 调试困难
- 堆栈跟踪问题
最佳实践:
- 只在性能关键路径使用
- 避免过度内联
- 保持函数体简单
- 考虑可维护性
总结
内联函数是 Kotlin 提供的强大性能优化工具,通过消除 lambda 对象创建和函数调用开销,显著提升性能。掌握内联函数、noinline、crossinline 和 reified 泛型的使用,对于编写高性能的 Kotlin 代码至关重要。
核心要点:
- inline - 消除 lambda 对象创建
- noinline - 阻止特定 lambda 内联
- crossinline - 禁止非局部返回
- reified - 运行时类型信息
- 性能权衡 - 代码膨胀 vs 性能提升