延迟初始化和密封类

延迟初始化

背景

Kotlin 提供了变量不可空特性来抑制空指针问题。

这个特性在保证程序安全的同时,也给我们带来了编码上的一些问题。

案例:试想一下,你的程序中有一个全局变量 a ,变量 a 创建之初并没有初始化,所以你令它等于 null。

假设 a 的类型为 String,因为要保证 a 非空,所以它的类型被声明为 String?

private var a: String? = null

接下来,每次需要调用 a 中的方法,都要进行判空处理(即使它早已被初始化),否则无法进行编译!

当代码中有越来越多的全局变量实例时,这个问题会愈发明显。

延迟初始化

为了解决这个问题,Kotlin 使用 lateinit 关键字对变量进行初始化。

private lateinit var a: String

lateinit 关键字告诉 Kotlin 编译器,我会在后续对该变量进行初始化,这样就不用将变量赋值为 null。

这样,在调用全局变量 a 中的方法时,就不用再进行判空处理。

在使用该对象时,需要保证其已被初始化,否则会出现运行时错误!

避免重复初始化

为了避免变量的重复初始化,可以使用以下代码

if (!::a.isInitialized) {
    
     // 判断 a 是否初始化,如果未初始化执行代码块
            a = "ming ming" // 对 a 进行初始化操作
        }

::object.isInitialized 是判断初始化的固定写法。

密封类

背景

新建一个 Result.kt 文件,在文件中编写以下代码:

interface Result
class Success(val message:String):Result
class Failure(val error: Exception):Result

代码中定义了一个 Result 接口,用于表示某个操作的执行结果。

定义两个类去实现 Result 接口,Success 类用于表示成功时的结果,Failture 类用于表示失败时的结果。

再定义一个 getResultMessage(),用于执行最终执行结果的信息,代码如下

fun getResultMessage(result: Result) = when(result){
    
    
    is Success -> result.message
    is Failure -> result.error.message
    else -> throw IllegalArgumentException()
}

getResultMessage() 方法接收一个 Result 参数。

我们用 when 语句来判断 Result 的类型,如果为 Success,打印成功消息,如果为 Failture 返回错误信息。

讲道理,此时应该结束代码。因为代码的执行结果要么成功,要么失败,并不存在其他情况。

但是为了满足 Kotlin 的语法检查,在最后写了一个 else 来抛出异常(实际上这个 else 是永远无法到达的)。

此外,如果我们新增了一个其他类继承 Result 接口,而忘了在 getResultMessage() 中添加对应的执行代码,

代码在执行时就会直接抛出 else 中的异常。

密封类

密封类可以很轻松地解决上述问题,声明一个密封类只需要在类前加上关键字 sealed。

将 Result 接口改造成密封类

sealed class Result 
class Success(val message:String):Result()
class Failure(val error: Exception):Result()

接下来在 when 中传入密封类做为条件,Kotlin 会检查该密封类有哪些子类,并强制要求你去处理每个子类所对应的条件,这样就可以保证即使没有编写 else 条件,也不会出现漏写分支的情况。

fun getResultMessage(result: Result) = when(result){
    is Success -> result.message
    is Failure -> result.error.message
}

当我们新建一个 Others 类继承 Result 密封类,getResultMessage() 会报错。

我们可以通过 Add remaining branches 来添加对所有子类对应条件的处理。

猜你喜欢

转载自blog.csdn.net/jiaweilovemingming/article/details/125063960