Skip to content

Kotlin 必备实践:每位开发者都该掌握的技巧(Part 2) #63

Description

@cnwutianhao

Part 1,介绍了 Kotlin 的基础最佳实践,例如不可变性、空安全、数据类、协程和密封类。

现在,更进一步。在这部分中,探讨有助于为生产级应用编写更简洁更安全更高效代码高级 Kotlin 最佳实践

对于高阶函数,优先使用内联函数

高阶函数可能会带来额外开销。当把函数作为参数传递时,使用 inline 关键字以避免不必要的对象创建并提升性能。

inline fun measureTime(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    println("Took ${System.currentTimeMillis() - start} ms")
}

不仅如此,这种技巧对于日志打印也尤为重要:

inline fun logD(tag: String, content: () -> String) {
    if (Debug) {
        println("$tag: ${content()}")
    }
}

logD("TAG") { "用户点击了 $something" }

传统的日志打印方式,无论日志是否最终输出,都会产生字符串拼接的开销。如果采用内联高阶函数的方式,不仅省去了拼接字符串的开销,还能省去高阶函数的额外开销。

使用类型别名(typealias)以提高可读性

如果你的函数签名变得冗长且重复,可使用类型别名(typealias)来简化它们。

typealias Callback = (Boolean, String?) -> Unit     // 简化冗长的 lambda 表达式
fun loginUser(username: String, password: String, callback: Callback) {
    // ...
}

typealias UserOrderMap = Map<String, OrderList>     // 简化冗长的泛型集合

这使你的 API 更简洁更易读

适当使用 const@JvmField

  • 对于编译时常量,使用 const val
  • 在 companion object 中,当 Java 互操作需要直接访问字段以避免 getter 开销时,使用 @JvmField
class ApiConfig {
    companion object {
        const val TIMEOUT = 30_000
        @JvmField val TAG = "ApiLogger"
    }
}

class Blog {
    @JvmField
    val id: Int = 123
}

在 Java 中访问时:

System.out.println(ApiConfig.TIMEOUT);
Blog blog = new Blog();
System.out.println(blog.id);

而如果没有加 @JvmField 的话,在 Java 中访问是这样的:

System.out.println(blog.getId());

利用默认参数与具名参数

与其创建多个重载,不如使用默认参数。

fun connect(host: String, port: Int = 8080, useSSL: Boolean = false) { ... }
connect("example.com")                // 默认端口 + 不启用 SSL
connect("example.com", useSSL = true) // 具名参数有助于提高可读性

在 Kotlin 中,非常建议开发者在进行函数调用的时候,加上参数的名字,这在对于函数的参数列表特别长的时候,有助于提高代码的可读性。

试想一下下面这个 Compose 函数(以下为 Jetpack Compose 中的一个典型复杂函数签名,用于演示命名参数的重要性):

@Composable
fun BasicTextField(
    state: TextFieldState,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    inputTransformation: InputTransformation? = null,
    textStyle: TextStyle = TextStyle.Default,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    onKeyboardAction: KeyboardActionHandler? = null,
    lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
    onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
    interactionSource: MutableInteractionSource? = null,
    cursorBrush: Brush = BasicTextFieldDefaults.CursorBrush,
    outputTransformation: OutputTransformation? = null,
    decorator: TextFieldDecorator? = null,
    scrollState: ScrollState = rememberScrollState(),
)

如果在调用的时候不加参数的名字,那么几乎每次看到这个函数调用,你都要跳转到文档你才能知道具体的功能。

避免在协程中滥用 GlobalScope

❗ 谨慎做法:

GlobalScope.launch { fetchData() }

✅ 推荐做法:

viewModelScope.launch { fetchData() }

通过 viewModelScopelifecycleScope 或自定义 CoroutineScope 使用结构化并发。这可确保协程进行生命周期感知的管理并防止泄漏。

优先使用不可变集合

默认使用 listOfmapOfsetOf 实现不可变性;只有在需要修改时才改用 mutableListOf

val numbers = listOf(1, 2, 3)               // 不可变
val mutableNumbers = mutableListOf(1, 2, 3) // 可修改

这个思路和优先使用 val 是一样的。不可变集合在并发环境中提高了安全性,同时也能降低阅读代码的难度。

使用 Result 类型进行错误处理

与其返回 null 或不必要地抛出异常,不如使用 Kotlin 内置的 Result

fun parseInt(str: String): Result<Int> {
    return runCatching { str.toInt() }
}
val number = parseInt("123")
    .getOrElse { 0 }    // 发生错误的回退

这使得错误处理变得显式且可组合,同时不会因为忘记 try-catch 导致系统崩溃。

编写具化(reified)泛型函数

Kotlin 允许将 inlinereified 泛型结合使用,从而在运行时访问类型信息。

Copy
inline fun <reified T> Gson.fromJson(json: String): T =
    this.fromJson(json, T::class.java)

val user: User = gson.fromJson(jsonString)

比手动传递 Class<T> 更简洁、更安全。

有效使用智能类型转换

与其显式地进行类型转换,不如通过 is 检查依赖 Kotlin 的智能类型转换。

fun printLength(obj: Any) {
    if (obj is String) {
        println(obj.length) // 智能类型转换为 String
    }
}

应用函数式编程惯用法

Kotlin 通过 mapfilterflatMapreduce 等操作鼓励函数式编程。

val evenSquares = (1..10)
    .filter { it % 2 == 0 }
    .map { it * it }

与手动循环相比,这会使代码更简洁、更具表达力

在集合上链式调用多个操作(如 map + filter + flatMap)时,会产生中间集合,带来额外内存开销。若操作较复杂或数据量较大,推荐使用 asSequence() 转为惰性序列,以减少中间产物,提升性能:

listOf(1,2,3,4,5).asSequence().map { it * 3  }.filter {  it > 2 }.flatMap { (0..it) }.toList()

总结

Kotlin 是一门强大的语言,为开发者提供了很高的灵活性。遵循这些高级最佳实践,开发者将能编写出高性能、安全且适用于生产环境的代码。

从内联函数到 Result 类型与结构化并发,这些技术将把你的 Kotlin 技能提升到新的层次。

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions