发布于2026-07-07 阅读(0)
扫一扫,手机访问
在 Ja va 里,lastName = firstName = "Matroskin" 这样的写法是合法的——因为赋值操作符 = 本身是表达式,会返回被赋的值,自然就能串起来。但 Kotlin 的设计者显然不想让赋值产生什么“意外副作用”,于是明确把赋值变成了语句(statement),没有返回值。所以你要是直接写 firstName = lastName = "Matroskin",编译期就会甩给你一句:“Assignments are not expressions, and only expressions are allowed in this context。”
不过话说回来,虽然不能照搬 Ja va 的链式赋语法,但 Kotlin 生态里从来不缺优雅的等效方案。下面这几种方式,既安全又符合 Kotlin 风格——你挑着用就好。
作用域函数是 Kotlin 的“招牌武器”之一。拿 let 来举例:把要赋的值丢进 let 的 lambda 里,然后一次性给多个属性赋值。看起来就像这样:
class Cat {
var firstName: String = ""
var lastName: String = ""
constructor() : this("") {
"Matroskin".let { name ->
firstName = name
lastName = name
}
}
// 如果构造逻辑简单,也可以直接:
constructor(name: String) {
firstName = name
lastName = name
}
}
用 let 的好处很明显:第一,避免了重复字面量;第二,天然支持空安全——把 "Matroskin" 换成 someString?.let { ... } 就行,完全无缝对接。
如果属性需要延迟初始化,或者牵涉到更复杂的逻辑,不妨用主构造参数配合 init 块:
class Cat(name: String) {
var firstName: String = name
var lastName: String = "" // 先占个位
init {
lastName = firstName // 复用已经初始化的值
}
}
这种写法的语义非常直白,没有额外开销,而且对 val 属性也友好(init 块里只能赋值一次,符合 val 的约束)。
技术上确实可以用 infix 扩展玩出链式效果,但说实话——这已经踩到 Kotlin 设计哲学的底线了。看看代码:
infix funT.`=`(prop: KMutableProperty ): T { prop.setter.call(prop, this) return this } // 使用时需要先声明可变属性: var firstName = "" var lastName = "" "Matroskin" `=` ::lastName `=` ::firstName // ❌ 编译失败:未初始化
问题一大把:属性必须提前给默认值(不然过不了空安全检查);::propertyName 引用的是实例属性,只能在对象创建后使用,没法在构造器里直接初始化;val 属性压根不支持(因为没有 setter);代码可读性差,调试时很难追踪副作用。
所以这个技巧,看看就好,就当了解 Kotlin 的底层机制——千万别往生产代码里放。
| 场景 | 推荐方式 |
|---|---|
| 简单统一赋值(比如构造时把同一个值赋给多个属性) | 主构造参数 + 多属性直接赋值(firstName = name; lastName = name) |
| 需要条件判断或复杂逻辑 | let / also 作用域函数 |
| 属性依赖其他属性的初始化结果 | init 块或 by lazy(针对 val) |
| 追求极致简洁且希望复用 | 封装成工厂函数,或放在伴生对象 companion object 里的 create() 方法 |
说到底,Kotlin 把赋值设计成语句而非表达式,不是缺陷,而是刻意的取舍——它拒绝隐式副作用,让代码的确定性更高。只要习惯了 Kotlin 原生的那套工具(作用域函数、init 块、by lazy 等),你会发现写出来的代码既安全又清晰,完全不输 Ja va 的链式赋值,甚至更好读。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8