Kotlin浅析延迟初始化与密封类的实现方法

目录
  • 一、lateinit延迟初始化关键字
  • 二、使用密封类优化代码

一、lateinit延迟初始化关键字

Kotlin中很多语法特性,如变量不可变,变量不可为空,等等 这些特性都是为了尽可能地保证程序安全而设计的,比如你的类中存在很多全局变量实例,为了保证它们的能够满足Kotlin的空指针检查语句标准,你不得不做非空判断保护,即使你非常确定它们不会为空。

下面距离看一下 :

class MainActivity : AppCompatActivity() {
    private var s: String? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        s = "test"
        Log.d("TAG", "onCreate: ${s!!.length}")
    }
}

我们将s 设置为了全局变量 , 但是它的赋值工作在onCreate()方法进行的,因此不得不将s赋值为null。

虽然你确定在打印前已经将s赋值成功,但是打印s的长度仍然要进行判空处理才行,否则编译不通过。

当你的代码中越来越多的全局变量实例时,这个问题就会变得越来越明显,到时候可能必须写大量额外判空处理的代码,却只是为了满足Kotlin的编译要求。

幸运的是,这个问题有解决办法的且非常之简单,就是对全局变量进行延迟初始化。

初始化使用的关键字 lateinit ,它可以高速编译器,我会在晚些时候对这个变量进行初始化,这样就不用一开始就赋值为null了。

class MainActivity : AppCompatActivity() {
    private lateinit var s: String
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        s = "test"
        Log.d("TAG", "onCreate: ${s.length}")
    }
}

可以看到,加上了lateinit关键字 ,这样一开始就不用赋值为null了,打印长度的时候也不用进行判空处理了,当然使用lateinit关键字 也不是没有风险,如果没有进行赋值,那么程序一定会崩溃,并抛出异常。

另外,我们可以通过代码来判断全局变量是否已完成了初始化工作,这样某些时候可以有效避免重复对某一个变量进行初始化操作。

class MainActivity : AppCompatActivity() {
    private lateinit var s: String
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (!::s.isInitialized) {
            s = "test"
        }
        Log.d("TAG", "onCreate: ${s.length}")
    }
}

二、使用密封类优化代码

新建一个Kotlin文件,代码如下 :

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

这里定义了一个Result接口,用于表示某个操作的执行结果,接口中没有编写任何内容。然后定义了两个类去实现Result接口:一个Success类用于表示成功时结果,一个Failure类用于表示失败时结果。

接下来再定义一个getResultMsg()方法 ,用于获取最后执行结果的信息,代码如下:

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

getResultMsg()方法中接受一个Result参数,我们通过when语句来判断,如果Result属于Success就返回成功的消息,如果Result是Failure就返回错误的信息,到目前为止代码是没什么问题的,烦人的是不得不写一个else条件语句,否则Kotlin编译不通过,其实代码永远也走不到else里 因为只有两种类型的存在,只是为了满足Kotlin的语法而已。

另外,编写else条件还有一个潜在的风险,如果我们新增一个Unknown类并实现了Result接口,用于表示未知的执行结果,但是忘记了在getResultMsg()方法中添加相应的条件,编译器不会提醒我们的,而是直接进入else条件里面去,从里面抛出异常并导致程序崩溃。

Kotlin的密封类很好的解决了此问题,密封类的关键字是sealed class ,它的用法同样很简单,我们可以轻松的将Result接口改造成密封类写法:

sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()
fun getResultMsg(result: Result) = when (result) {
    is Success -> result.msg
    is Failure -> result.error.message
}

代码没什么变化,只是将interface改成了 sealed class。密封类是一个可继承的类,因此在继承它的时候还需要加上一对括号。

密封类的优点是,可以再getResultMsg() 方法中取消else条件语句。

为什么取消掉else条件语句还能编译通过呢,Kotlin编译器会自动检查该密封类有哪些子类,并强制要求将每一个子类所对应的条件全部处理。这样就可以保证,即使没有else条件,也不可能出现漏写的情况,如果现在新增一个Unknown类,并也让它继承自Result,此时getResultMsg()方法就一定会报错,必须添加Unknown语句条件才能编译通过。

注意:密封类及其所有子类只能定义同一个文件的顶层位置,不能嵌套在其他类中,这也是被密封类底层实现机制所限制的。

到此这篇关于Kotlin浅析延迟初始化与密封类的实现方法的文章就介绍到这了,更多相关Kotlin延迟初始化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Kotlin修饰符lateinit(延迟初始化)案例详解

    Kotlin定义变量一般有如下写法 lateinit var name: String var age: String? = null 那么用lateinit 修饰和下面那种有什么区别呢,我们来看一下这两行代码反编译成java代码是什么样子的. @NotNull public String name; @Nullable private String age; @NotNull public final String getName() { String var10000 = this.name

  • Kotlin浅析延迟初始化与密封类的实现方法

    目录 一.lateinit延迟初始化关键字 二.使用密封类优化代码 一.lateinit延迟初始化关键字 Kotlin中很多语法特性,如变量不可变,变量不可为空,等等 这些特性都是为了尽可能地保证程序安全而设计的,比如你的类中存在很多全局变量实例,为了保证它们的能够满足Kotlin的空指针检查语句标准,你不得不做非空判断保护,即使你非常确定它们不会为空. 下面距离看一下 : class MainActivity : AppCompatActivity() { private var s: Str

  • kotlin延迟初始化和密封类详细讲解

    目录 对变量延迟初始化 使用密封类优化代码 对变量延迟初始化 Kotlin语言有许多特性,包括变量不可变,变量不可为空,等等.这些特性都是为了尽可能地保证程序安全而设计的,但是有些时候这些特性也会在编码时给我们带来不少麻烦. 比如,你的类中存在许多全局变量实例,为了保证他们能够满足kotlin的空指针检查语法标准,你不得不做出许多的非空判断保护才行,即使你非常确定它们不会为空. 通过一个例子: class MainActivity:AppCompatActivity(),View.OnClick

  • kotlin浅析when与循环的使用

    目录 使用区间 When 表达式 For 循环 返回和跳转 标签 Break和Continue 标签处返回 使用区间 使用 in 运算符来检测某个数字是否在指定区间内,区间格式为x..y: 实例 fun main(args: Array<String>) { val x = 5 val y = 9 if (x in 1..8) { println("x 在区间内") } } When 表达式 when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件. when 既

  • Kotlin浅析null操作方法

    1.在java中由于null引起的空指针异常,是一个运行时异常. 在kotlin中为了避免这样的问题,会在编译期提示出来,而不是在运行期才报错. 1)比如我们把null赋值给一个已经被赋值的变量或者定义一个返回null的函数,编译器就会报错提示:Null can not be a value of a non-null type String var hello = "hello world" hello = null fun getString(): String{ return n

  • 浅析Python 简单工厂模式和工厂方法模式的优缺点

    前言 在<设计模式>一书中工厂模式提到了: 工厂方法模式(Factory Method) 抽象工厂模式 (Abstract Factory) 但是在实际过程中还有一种工厂模式经常被使用,那就是 简单工厂模式(Simple Factory).有一种常见的分类的方法:根据产品是由具体产品还是具体工厂可以分为 工厂方法模式 和 简单工厂模式:根据工厂的抽象程度可以分为 工厂方法模式 和 抽象工厂模式.接下来会通过例子对比简单工厂模式和工厂方法模式. 工厂意图 定义一个用于创建对象的接口,让子类决定实

  • JavaScript使用setTimeout实现延迟弹出警告框的方法

    本文实例讲述了JavaScript使用setTimeout实现延迟弹出警告框的方法.分享给大家供大家参考.具体如下: 下面的代码执行后点击按钮会延迟3秒钟弹出一个警告框,主要演示了setTimeout的使用方法 <!DOCTYPE html> <html> <body> <p> Click the button to wait 3 seconds, then alert "Hello". </p> <button onc

  • 浅析jquery数组删除指定元素的方法:grep()

    遇到的问题 今天遇到一个问题,删除数组中的一个指定元素,并返回新的数组. 我定义的js数组是这样的: var sexList=new Array[3]; sexList[0]="1"; sexList[1]="2"; sexList[2]=""; 想达到的效果 我想达到的效果是这样的: 删除索引=1的元素,并返回新数组. 返回的结果是: var sexList=new Array("1",""); 我们知道

  • C#中的应用程序接口介绍及实现,密封类与密封方法

    API  Application Programming Interface 应用程序接口 接口 定义 :指描述可属于任何类或结构的一组相关功能. 接口的成员可以是方法(不能有方法体),属性,事件和索引器,但不能包含常数,字段,运算符,实例构造函数析构函数或类,也不能包括任何种类的静态成员,接口中的成员不允许添加访问修饰符,(默认都是public) 简介: 1. 接口是一个引用类型,通过接口可以实现多重继承. 2. C#中接口的成员不能有new.public.protected.internal

  • Spring初始化和销毁的实现方法

    这篇文章主要介绍了Spring初始化和销毁的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 指定初始化和销毁方法 通过@Bean指定init-method和destroy-method: @Bean(initMethod="init",destroyMethod="detory") public Car car(){ return new Car(); } 二 通过让Bean实现Initializing

随机推荐