Kotlin基础回顾:空安全、扩展函数、高阶函数与Lambda表达式
好,咱们直接进入正题。这一章我打算聊聊Kotlin里几个最基础、但也是最核心的特性。说实话,很多从Java转过来的同学,一开始会觉得这些语法有点「怪」。但用久了你会发现,这些设计是真的香。
1. 空安全:别再让NullPointerException背锅了
先说说空安全。Java里那个经典的NPE,我估计大家都被坑过。我记得有一次线上崩溃,查了半天,就是一个简单的对象没判空。嗯,从那以后我写Java都养成了「万物皆判空」的强迫症。
Kotlin的做法很直接——它在类型系统层面就把空和非空分开了。
// 非空类型
var name: String = "Kotlin"
// name = null // 编译报错
// 可空类型
var nullableName: String? = "Kotlin"
nullableName = null // 没问题
你想想看,这相当于在编译期就帮你堵住了大部分空指针的坑。我个人习惯是:能用非空类型就绝不用可空类型。实在避免不了,再用安全调用操作符 ?.。
核心操作符速查表
| 操作符 | 含义 | 示例 |
|---|---|---|
?. |
安全调用 | str?.length |
?: |
Elvis 操作符 | str?.length ?: 0 |
!! |
非空断言 | str!!.length |
避坑指南:我曾经在代码里滥用 !!,觉得「这里肯定不为空」。结果上线后啪啪打脸。现在我的原则是:!! 只用在明确知道不会为空的场景,比如从Bundle取数据且你确定之前存过。其他情况,老老实实用 ?. 或 ?:。
2. 扩展函数:给别人的类加点「私货」
扩展函数这个特性,说白了就是让你能给一个已经存在的类添加新方法,又不用继承它。这在Android开发里特别实用。
举个例子,我们经常需要把dp转成px。以前在Java里,你得写个工具类,然后到处调用 DensityUtil.dp2px(context, 16f)。在Kotlin里,你可以这样:
fun Int.dp2px(context: Context): Int {
return (this * context.resources.displayMetrics.density).toInt()
}
// 使用
val px = 16.dp2px(context)
你看,直接给 Int 类型加了个方法。我个人觉得,扩展函数最大的价值在于:它让代码读起来更像「自然语言」。我在项目中经常用扩展函数来封装一些重复的UI操作,比如给View设置可见性:
fun View.visible() {
visibility = View.VISIBLE
}
fun View.gone() {
visibility = View.GONE
}
// 使用
button.visible()
textView.gone()
小技巧:扩展函数是静态解析的,不是运行时动态分发的。也就是说,它本质上就是个静态方法。所以别指望它能实现多态效果。这一点我刚开始学的时候也迷糊过。
3. 高阶函数与Lambda:把函数当参数传
高阶函数,说白了就是「能接收函数作为参数,或者能返回一个函数」的函数。Lambda则是那种「匿名函数」的简洁写法。
为什么会需要这个?你想想看,在Java里我们写点击事件,得搞个匿名内部类。代码又长又啰嗦。Kotlin里一个Lambda就搞定了。
// Java 写法
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
});
// Kotlin Lambda 写法
button.setOnClickListener { v ->
// do something
}
这还不是最爽的。高阶函数真正的威力在于「抽象控制流程」。比如我们经常需要做「在IO线程执行,在主线程回调」的操作:
fun <T> asyncTask(
onBackground: () -> T,
onMain: (T) -> Unit
) {
thread {
val result = onBackground()
runOnUiThread {
onMain(result)
}
}
}
// 使用
asyncTask(
onBackground = {
// 网络请求或数据库操作
"result"
},
onMain = { result ->
// 更新UI
textView.text = result
}
)
我在项目中重构过一个网络请求模块,就是用这种高阶函数把「异步+回调」的模式封装起来的。代码量直接砍了一半,而且逻辑清晰了很多。
Lambda语法要点
- 如果Lambda是最后一个参数,可以提到括号外面
- 如果只有一个参数,可以用
it隐式表示 - 如果Lambda是唯一参数,括号都可以省略
// 完整写法
list.filter({ item -> item.length > 3 })
// 简化写法
list.filter { it.length > 3 }
4. 实战:把这些特性串起来
光讲理论没意思。我拿一个实际场景来演示一下——给RecyclerView的Adapter写一个通用的点击事件处理。
// 扩展函数:给ViewHolder添加点击能力
fun <T> RecyclerView.ViewHolder.onItemClick(
item: T?,
action: (T) -> Unit
) {
itemView.setOnClickListener {
item?.let { nonNullItem ->
action(nonNullItem)
}
}
}
// 使用
class MyAdapter : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.onItemClick(item) { clickedItem ->
// 这里处理点击事件
Toast.makeText(context, clickedItem.name, Toast.LENGTH_SHORT).show()
}
}
}
你看,这里用到了扩展函数、空安全(?. 和 let)、高阶函数和Lambda。几个特性一组合,代码既简洁又安全。
个人经验:刚开始学Kotlin的时候,别急着把所有代码都改成Lambda。我建议先写完整写法,等熟悉了再逐步简化。不然一上来就各种 it、省略括号,自己都容易看晕。
好了,这一章的内容就到这里。空安全、扩展函数、高阶函数和Lambda,这几个概念是Kotlin的基石。理解透了,后面学协程、Flow这些高级特性会轻松很多。下一章我们聊聊集合操作符,那个也是Kotlin的拿手好戏。