问题
开源的雨燕输入法项目已经切换成Kotlin
语言进行开发。
代码切换完成后,在重构代码时发现,使用Kotlin
语言定义全局变量(包括View对象),默认只均为null
。
这就导致所有使用该变量的调用均需进行判空(?.
或!!
)操作,满足 Kotlin 的空指针检查,哪怕已经明确该变量此时不会为null
。
此时就需要使用关键字lateinit
救场了。
优化前
代码未优化前,创建变量及调用方式如下。
创建全局变量mSkbRoot,初始值为
null
。var mSkbRoot: RelativeLayout? = null
代码中为全局变量mSkbRoot赋值。
mSkbRoot = LayoutInflater.from(context).inflate(R.layout.sdk_skb_container, this, false) as RelativeLayout
使用全局变量,需进行空指针检查。
mIvSkbMove = mSkbRoot!!.findViewById(R.id.iv_keyboard_move) mSkbRoot?.background = activeTheme.backgroundGradientDrawable(isKeyBorder)
若未检查空指针,编辑器会报错:
Smart cast to 'RelativeLayout' is impossible, because 'mSkbRoot' is a mutable property that could have been changed by this time
优化后
lateinit
关键字用于延迟属性初始化,通知编译器该变量会在后续进行初始化,不用在定义时为其赋值null
。lateinit
可以对可变、非空、非基本数据类型进行修饰。
使用lateinit
修饰后,我们就可以像Java
类似的调用。
创建全局变量,mSkbRoot,使用
lateinit
修饰。lateinit var mSkbRoot: RelativeLayout
为全局变量mSkbRoot赋值。
mSkbRoot = LayoutInflater.from(context).inflate(R.layout.sdk_skb_container, this, false) as RelativeLayout
使用全局变量,无需空指针检查。
mIvSkbMove = mSkbRoot.findViewById(R.id.iv_keyboard_move) mSkbRoot.background = activeTheme.backgroundGradientDrawable(isKeyBorder)
问题记录
异常风险。
通过lateinit
可以实现延迟对变量进行初始化,但该方式同时会带来未及是对变量初始化,而直接调用变量导致的异常UninitializedPropertyAccessException
。判空操作失效。
优化前,我们可以对全局变量进行判空操作:if (mSkbRoot == null) { // 初始化操作 }
但是一旦使用
lateinit
关键字修饰后,该判断会失效:Condition 'mSkbRoot == null' is always 'false'
。
此时该变量不为null
,但是未进行初始化,我们需要对变量判断是否进行初始化:if (::mSkbRoot.isInitialized) { // 初始化操作 }