lateinit 延迟初始化
一般情况下,Kotlin 的属性要求我们在声明时必须初始化给它一个值。但是有些时候,我们可能在生命周期还未执行的时候,无法获取到必要的信息,无法完成初始化。此时我们必须要将这个属性声明成 可空类型 ,然后初始化为 null 。等到执行到对应的生命周期获取到必要信息以后,对其再次进行赋值。但是在后面的生命周期中,虽然我们已经明确对这个属性进行了赋值,但是因为它是个 可空类型 ,所以每次我们访问这个属性时还都需要进行判空处理,这无疑是很让人难受的。具体的代码场景如下:
class MainActivty: Activity {private var prop: String? = nulloverride fun onCreate(savedInsatnceBundle: Bundle?) {val param = intent.getStringExtra("param")prop = param}override fun onResume() {// 使用时if(prop != null) print(prop)// 或者使用 ?print(prop?.length)}}
为了解决这个问题,Kotlin 提供了 lateinit 关键字。使用 lateinit 关键字声明想要延迟初始化的属性,就可以在声明属性时,使用不可空类型并不用初始化为 null ,然后在使用属性时,就可以避免编写这种判空的逻辑了。具体的代码如下:
class MainActivty: Activity {private lateinit var prop: Stringoverride fun onCreate(savedInsatnceBundle: Bundle?) {val param = intent.getStringExtra("param")prop = param // 完成初始化}override fun onResume() {// 使用时不必判空print(prop.length)}}
虽然这样会让我们的代码写起来更加的方便,但是也对开发者提出了要求:一定要保证在使用属性前对属性完成了初始化赋值!如果没有初始化就直接使用了属性,那么会抛出 UninitalizedPropertyAccessException 异常。
当然如果我们想要编写一些防御性代码,避免抛出这样的异常引发程序崩溃的话,我们可以通过下面的代码进行判断:
class MainActivty: Activity {private lateinit var prop: Stringoverride fun onCreate(savedInsatnceBundle: Bundle?) {val param = intent.getStringExtra("param")prop = param // 完成初始化}override fun onResume() {// 使用时的防御性判断,看是否完成了初始化if(::prop.isInitialized) {print(prop.length)}}}
密封类
密封类是 Java 中没有的一个概念,Kotlin使用 sealed 关键字来修饰一个类,这个类就被称为密封类,一般会与Kotlin的 when 代码块搭配使用。
常见的使用场景如下:
sealed class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)class LeftViewHolder(itemView: View): MyViewHolder(itemView) {// ...}class RightViewHolder(itemView: View): MyViewHolder(itemView) {// ...}class MyAdapter(list: List<Any>): RecyclerView.Adapter<MyViewHolder> {// ...override fun onBindViewHolder(holder: MyViewHolder, position: Int) {val data = list[position]when(holder) {is LeftViewHolder -> holder.left.text = datais RightViewHolder -> holder.right.text = data}}}
可以看到,我们定义了一个 RecyclerView.ViewHolder 的子类作为密封类。然后继承这个密封类,创建了两个子类 LeftViewHolder 和 RightViewHolder 。当我们在 RecyclerView.Adapter 中绑定 ViewHolder 的数据时,通过 when 来判断当前的 holder 是哪一种 ViewHolder,然后调用它们中绑定数据的方法。
此时,如果我们增加了一种ViewHolder,叫做 CenterViewHolder 。如果是在 Java 中,我们需要在定义完 CenterViewHolder 类之后,自己寻找需要更改代码的地方(也就是 when 代码块)。但是使用了密封类的话,如果增加了密封类的子类,那么如果 when 代码块的分支条件如果没有将密封类的子类覆盖完全,那么IDE就会报错。更改后的代码如下:
class CenterViewHolder(itemView: View): MyViewHolder(itemView) {// ...}class MyAdapter(list: List<Any>): RecyclerView.Adapter<MyViewHolder> {// ...override fun onBindViewHolder(holder: MyViewHolder, position: Int) {val data = list[position]when(holder) {is LeftViewHolder -> holder.left.text = datais RightViewHolder -> holder.right.text = data// 不加这一个分支,when代码块会报错is CenterViewHolder -> holder.center.text = data}}}
另外再多说一句,密封类及其子类只能定义在同一个文件的顶层位置,不能嵌套在其他类中,这是被密封类的底层实现所限制的。
