首页 > 其他分享 >Kotlin 空指针检查

Kotlin 空指针检查

时间:2024-03-22 12:31:40浏览次数:32  
标签:函数 检查 Kotlin 编译 let 操作符 指针

目录

引言

正文

空指针检查机制

可为空的类型系统

判空辅助工具

 ?.操作符

 ?:操作符

非空断言工具

 !!操作符

与众不同的辅助工具—let


引言

        某国外机构做过一个统计,Android系统上崩溃率最高的异常类型就是空指针异常(NullPointerException),空指针是一种不受编程语言检查的运行时异常,只能由程序员主动通过逻辑判断来避免。

        Kotlin却非常科学地解决了这个问题,它利用编译时判空检查的机制几乎杜绝了空指针异常。虽然编译时判空检查的机制有时候会导致代码变得比较难写,但是Kotlin提供了 一系列的辅助工具,让我们能轻松地处理各种判空情况(和Dart挺相似的)。

正文

空指针检查机制

fun doStudy(study: Study) {
    study.readBooks()
    study.doHomework()
}

        Kotlin写法虽然看上去似乎和Java没有什么区别,但实际上它是没有空指针风险的,因为Kotlin默认所有的参数和变量都不可为空。所以这里传入的Study参数也一定不会为空,我们可以放心地调用它的任何函数。

假设在调用doStudy方法传null会发生什么呢?

编译报错。Kotlin将空指针异常的检查提前到了编译时期,修正之后才能成功运行,这样就可以保证程序在运行时期不会出现空指针异常了。

可为空的类型系统

        那如果业务逻辑就是需要某个参数或者变量为空呢?Kotlin提供了另外一套可为空的类型系统,只不过在使用可为空的类型系统时,我们需要在编译时期就将所有潜在的空指针异常都处理掉,否则代码将无法编译通过。

        在类名的后面加上一个问号。比如,Int表示不可为空的整型,而Int?就表示可为空的整型;String表示不可为空的字符串,而String?就表示可为空的字符串。

图1.1

        由于将参数改成了可为空的Study?类型,此时调用参数的readBooks()和doHomework()方法都可能造成空指针异常,因此Kotlin在这种情况下不允许编译通过。

        怎么解决呢?使用if判空固然可以,如果每处检查代码都使用if判断语句,则会让代码变得比较啰嗦,而且if判断语句还处理不了全局变量的判空问题。

判空辅助工具

 ?.操作符

        当对象不为空时正常调用相应的方法,当对象为空时则什么都不做。

 ?:操作符

        这个操作符的左右两边都接收一个表达式, 如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。

val c = if (a != null) a else b

|
|
v

val c = a ?: b

//实例
fun getTextLength(text: String?) = text?.length ?: 0

非空断言工具

 !!操作符

        Kotlin的空指针检查机制也并非总是那么智能,有的时候我们可能从逻辑上已经将空指针异常处理了,但是Kotlin的编译器并不知道,这个时候它还是会编译失败。

        这段代码一定是无法运行的。因为printUpperCase()函数并不知道外部已经对content变量进行了非空检查,在调用toUpperCase()方法时,还认为这里存在空指针风险,从而无法编译通过。        

        如果我们想要强行通过编译,可以使用非空断言工具,写法是在对象的后面加上!!    

fun printUpperCase() {
    val upperCase = content!!.toUpperCase()
    println(upperCase)
}

        这是一种有风险的写法,意在告诉Kotlin,我非常确信这里的对象不会为空,所以不用你来帮我做空指针检查了,如果出现问题,你可以直接抛出空指针异常,后果由我自己承担。

与众不同的辅助工具—let

        let既不是操作符,也不是什么关键字,而是一个函数。这个函数提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中。

实例:

obj.let { obj2 ->
 // 编写具体的业务逻辑
}

        这里调用了obj对象的let函数,然后Lambda表达式中的代码就会立即执行,并且这个obj对象本身还会作为参数传递到Lambda表达式中。不过,为了防止变量重名,这里将参数名改成了obj2,但实际上它们是同一个对象,这就是let函数的作用。

        let函数和空指针检查有什么关系呢?其实let函数的特性配合?.操作符可以在空指针检查的时候起到很大的作用。

        以图1.1代码举例,虽然使用了?.操作符简化了if的写法,但是准确翻译为if的写法,实际却进行了两次判断,表达方式也有点啰嗦。

使用let函数结合?.操作符:

fun doStudy(study: Study?) {
    study?.let {
        it.readBook()
        it.doHomework()        
    }
}

        ?.操作符表示对象为空时什么都不做,对象不为空时就调用let函数,而let函数会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了,我们就能放心地调用它的任意方法了(Lambda写法简化了,省去了传入let函数体中的参数声明,详见)。

        let函数是可以处理全局变量判空的问题。下图中使用if判断任然报错的原因:全局变量的值随时都有可能被其他线程所修改,即使做了判空,仍无法保证没有空指针风险,而这也体现了let函数的优势。

标签:函数,检查,Kotlin,编译,let,操作符,指针
From: https://blog.csdn.net/canada_numbfish/article/details/136809058

相关文章

  • dxGaugeControl指针仪表盘
    界面放上dxGaugeControl控件,点右键添加比例->圆选择一个自己喜欢的样子,我这只是为了要指针 打开视图选项AngleStart和AngleEnd主要是调整0-100位置的MinValue,MaxValue是刻度最大、最小值MinorTickCount设置次刻度数量ShowBackground是否显示背景图片SetShowFirstTick、......
  • C++开发基础——智能指针
    一,智能指针1.智能指针简介智能指针是用法和行为类似于指针的类对象。智能指针的底层对原始指针做了一定的封装。智能指针除了像指针一样可以存储变量的地址,还提供了其他功能,比如可以管理动态内存分配,对引用进行计数等。当智能指针所指向的变量离开了作用域或被重置时,智能......
  • Optional避免空指针
    1/**2*<h1>学会Optional,规避空指针异常</h1>3**/4@SuppressWarnings("all")5publicclassOptionalUsage{67privatestaticvoidbadUsageOptional(){89Optional<User>optional=Optional.ofNullable(nu......
  • 深入理解指针
    1、内存和地址1.1内存在生活中相当于一栋楼中房间号,在计算机中cpu,在处理数据时,需要的数据就是在内存中读取的,处理以后的数据也会放在内存中,内存会被划分成一个一个的内存单元,每个内存单元的大小取一个字节一个比特位可以存储一个2进制的为1或0;bit——比特位;Byte——字节;1B......
  • 【Azure Policy】使用Azure Policy来检查Azure资源名称是否满足正确要求(不满足就拒绝
    问题描述使用AzurePolicy来检查Azure资源名称是否满足正确要求,如果不满足就拒绝创建或标记为不合规non-compliance在创建Azure上资源的时候,有如下需求:1)资源的名称必须以一个前缀开头,如prod,test等。2)资源的名称结尾处必须是一个数字,如0,1,2,3,4,5,6,7,8,9。3)如果不合规,则拒绝新......
  • C++ this指针
    1. this指针的用处一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时......
  • C语言(一级指针)
    指针本质:地址一级指针一级指针变量名:存储普通变量的地址格式:存储类型        数据类型        *指针变量名                                int                *pinta=5;int*p=&a;--------......
  • Kotlin,简直是 Java 的 Pro Max!(笔记3 进阶篇)
    目录拓展拓展函数拓展属性运算符重载operator高阶函数通过高阶函数,模拟实现标准函数apply内联函数inlinenoinlinecrossinline泛型泛型类泛型方法限定泛型类型模拟实现apply标准函数(泛型版)泛型高级特性回顾Java中的协变和逆变Kotlin的协变和逆变委托......
  • C语言的指针详解
    一、指针的定义及使用1.指针是什么?指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以......
  • 拿捏指针(三)
    ✨✨欢迎......