热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

Kotlin语言的许多特性,包括变量不可变,变量不可为空,等等。这些特性都是为了尽可能地保证程序安全而设计的,但是有些时候这些特性也会在编码时给我们带来不少的麻烦,下面我们来了解延迟初始化和密封类的特点

一、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延迟初始化内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了Java中Currency类的getInstance()方法,该方法用于检索给定货币代码的该货币的实例。文章详细解释了方法的语法、参数、返回值和异常,并提供了一个示例程序来说明该方法的工作原理。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • Iamtryingtocreateanarrayofstructinstanceslikethis:我试图创建一个这样的struct实例数组:letinstallers: ... [详细]
author-avatar
suny
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有