如何确保初始为Null的属性仅被设置非Null值且设值后执行操作?
你的两个方案都完全可行,各有优劣势,我来帮你梳理清楚:
一、自定义Setter函数方案
这个方案逻辑简单直接,完全能满足你的需求:
- 你把内部的nullable变量
initialNull做了私有封装,对外只暴露接收非null参数的setInitial方法,从语法层面就把null值挡在了外面,根本不会有null被设置进去的可能; - 在
setInitial方法里,赋值后直接调用onInitialSet(),流程清晰易懂。
不过有个小细节可以优化下,比如把变量的setter设为私有,避免内部不小心直接赋值null,同时让代码更符合Kotlin的属性风格:
class Test { var initialNull: String? = null private set // 禁止外部甚至内部直接给这个属性赋值 fun setInitial(nonNullValue: String) { field = nonNullValue onInitialSet() } private fun onInitialSet() { // 这里写设置后的操作逻辑 } }
唯一的小缺点是,外部调用的时候得用方法调用(test.setInitial("xxx")),而不是Kotlin常用的属性赋值语法(test.initialNull = "xxx"),如果在意代码风格的一致性,可能会有点不习惯,但功能上完全没问题。
二、自定义ReadWriteProperty委托方案
这个方案同样可行,而且更贴合Kotlin的委托属性设计,体验更好:
- 你可以封装一个通用的委托类,专门处理“初始为null、后续仅能设置非null值+设置后回调”的场景,还能在多个类里复用这个逻辑;
- 外部使用的时候,完全是普通属性的赋值语法,代码更简洁优雅。
给你写个示例实现:
import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty class NonNullableSetterObservable<T : Any>( initialValue: T?, private val onSetCallback: (oldValue: T?, newValue: T) -> Unit ) : ReadWriteProperty<Any?, T?> { private var backingValue: T? = initialValue override fun getValue(thisRef: Any?, property: KProperty<*>): T? { return backingValue } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) { // 直接拦截null值,要么抛出异常要么根据需求处理 val nonNullValue = value ?: throw IllegalArgumentException("Cannot assign null to this property") backingValue = nonNullValue onSetCallback(backingValue, nonNullValue) } } // 使用示例 class Test { var initialNull: String? by NonNullableSetterObservable(null) { oldValue, newValue -> onInitialSet() } private fun onInitialSet() { // 执行设置后的操作 } }
这个委托类在setValue阶段就会检查传入的值,如果是null直接抛出异常,确保只有非null值能被设置,同时触发你需要的回调逻辑。如果不想抛出异常,也可以改成忽略null赋值或者做其他处理,灵活性很高。
总结
- 如果你的需求只在单个类里用,追求简单直接,自定义Setter函数的方案足够用;
- 如果需要在多个类复用这个逻辑,或者更偏好Kotlin的属性赋值语法,那自定义委托的方案是更好的选择。
内容的提问来源于stack exchange,提问作者Derekyy
相关产品推荐
相关产品推荐

