很多同学刚上手使用Kotlin知道它有针对Java NullPointerException的管理,而在Kotlin中?和!!均是和NullPointerException有关系,可他们的区别到底是什么呢?为什么别人开发的项目中出现了好多"?",而我读起来却满脸问号。

 
 

不懂就问百度呀,确实有人解释它们的区别,比如:

 
 

这是输入"kotlin ?和!!"搜索到的百度第一条答案,确实这位说的没错。不过我觉得对于一个刚接触KT(Kotlin)的新手来说,他恐怕需要有汉语八级才能透彻理解这两句话的意思。

先阐述两个概念:
"?"加在变量名后,系统在任何情况不会报它的空指针异常。
"!!"加在变量名后,如果对象为null,那么系统一定会报异常!

先拿Java代码举个例子

        ArrayList<String> myList = null;     //  创建一个null的队列
Log.d("TAG", "-->> List Size = " + myList.size());

这个例子中,执行到Log打印队列长度时,大家都知道系统一定会报NullPointerException。然而如果在KT中,在调用myList的时候在它后面加上一个问号myList?.size(),当myList为null的时候直接会打印List Size = null并不会有null异常出现。

当使用Android Studio把上面那段Java自动转换成KT代码写法后:

        val myList : ArrayList<String>? = null
Log.d("TAG", "-->> List Size = ${myList!!.size}")

编译器为什么自动把myList.size()变成了myList!!.size呢,为什么加上的是感叹号不是问号。

这是因为编译器在转化时为了保证代码转化前后的一致性所造成的。换句话说,在Java上出异常的,转化到KT上,编译器任然会让他保持抛出异常,NullPointerException也是如此。

所以结合上下文可以看得出,!!加上去后好像并没有和之前Java代码有什么区别嘛,该null的地方任然会抛出异常。所以大多数情况下都会使用?来检测null,轮不到!!出场。!!只会在你需要对某对象进行非空判断,并且需要抛出异常时才会使用到。

那我们接下来着重讲解一下?到底怎么用。
在声明对象时,把它跟在类名后面,表示这个类允许为null;
在调用对象时,把它跟在对象后面,表示如果为null程序就会视而不见。
如下列代码:

    // 这是声明一个变量,问号跟在类名后面
var room: Room? = Room() private fun checkRoom() {
// 因为加上了问号,所以可以任意的把room变成空
room = null // 因为在调用时加上了问号,所以程序不会抛出异常
Log.d("TAG", "-->> room name = ${room?.roomName}")
}

再举个不用?的例子:

    // 这样程序就默认的给room加上了!!,从此以后room不允许为null
var room: Room = Room() private fun checkRoom() {
// 当把null赋给room时,从编译的时候就已经不通过
room = null
// 并且编译器建议把对象后面的问号删除,因为这个对象永远不为空
Log.d("TAG", "-->> room name = ${room.roomName}")
}

所以加上?是一种安全的写法,它体现了Kotlin null safety的特性。
KT的语法很灵动,定义参数还可以写成

    val room: Room? = Room()    // 先实例化一个room,并且room可以为空
val room: Room? = null // 不实例化了,开始room就是空的 val room: Room = Room() // 实例化一个room,并且room永远不能为空
val room = Room() // 和上一行代码一样,是KT最常用的简写语法

然而加上问号以后程序就万事大吉永远摆脱了NullPointerException的烦恼?我们再看下一段代码:

        val roomList: ArrayList<Room>? = null
if (roomList?.size > 0) {
Log.d("TAG", "-->> 房间数不是0")
}

当我们判断list.size的时候,编译器会告诉我们"Operator call corresponds to a dot-qualified call 'roomList?.size.compareTo(0)' which is not allowed on a nullable receiver 'roomList?.size'."。大概意思是,当roomList为null的时,它的size返回就是"null",但是"null"不可以和int值比大小,所以编译器建议我们写成roomList?.size!! > 0

没错,经过编译器的建议加上了!!,我们程序运行到这行代码,roomList为null时它一定会报异常。所以是不是必须得在外面套一层if(roomList != null)这种Java常见语句才能避免异常吗?

当然Kotlin不会让程序出现这种啰嗦的代码,所以里面提供了对象A ?: 对象B表达式,并且取消了Java中的条件表达式 ? 表达式1 : 表达式2这个三元表达式。

?:表示的意思是,当对象A值为null的时候,那么它就会返回后面的对象B。

        val roomList: ArrayList<Room>? = null
val mySize= roomList?.size ?: 0

此时mySize的值就为0,因为roomList?.size为空。

所以我们可以把上面的代码改成这样:

        val roomList: ArrayList<Room>? = null
if (roomList?.size ?: 0 > 0) { // 这一行添加了?:
Log.d("TAG", "-->> 房间数不是0")
}

就目前为止使,用上面的??:基本上能避免程序中出现的所有NullPointerException。

作者:Jason_Samuel
链接:https://www.jianshu.com/p/51b2e5aa3dd8
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Kotlin中?和!!的区别的更多相关文章

  1. kotlin中“==”和“===”的区别

    code 1 fun main(args: Array<String>) { val a : Int = 1000 println(a == a) //true println(a === ...

  2. Kotlin中的object 与companion object的区别

    之前写了一篇Kotlin中常量和静态方法的文章,最近有人提出一个问题,在companion object中调用外部的成员变量会调用不到,这才意识到问题,本篇文章会带着这个疑问来解决问题. 一. obj ...

  3. Kotlin中与Java不同的地方 需要注意

    1. 在Kotlin中不会将基本数据类型的自动转型比如 scriptIntrinsicBlur.setRadius(25) //报错, 必须写成 25f 或者 调用.toFloat() 2.Kotli ...

  4. Kotlin中常量和静态方法

    常量 Java中: class StaticDemoActivity { public static final String LOAN_TYPE = "loanType"; pu ...

  5. 浅谈Kotlin中的函数

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/UV23Uw_969oVhiOdo4ZKAw作者:连凌能 Kotlin,已经被Android官方 ...

  6. kotlin中的嵌套类与内部类

    Java中的内部类和静态内部类在Java中内部类简言之就是在一个类的内部定义的另一个类.当然在如果这个内部类被static修饰符修饰,那就是一个静态内部类.关于内部类 和静态内部类除了修饰符的区别之外 ...

  7. Kotlin中变量不同于Java: var 对val(KAD 02)

    原文标题:Variables in Kotlin, differences with Java. var vs val (KAD 02) 作者:Antonio Leiva 时间:Nov 28, 201 ...

  8. MyBatis Mapper.xml文件中 $和#的区别

    MyBatis Mapper.xml文件中 $和#的区别   网上有很多,总之,简略的写一下,作为备忘.例子中假设参数名为 paramName,类型为 VARCHAR . 1.优先使用#{paramN ...

  9. (实用篇)PHP中unset,array_splice删除数组中元素的区别

    php中删除数组元素是非常的简单的,但有时删除数组需要对索引进行一些排序要求我们会使用到相关的函数,这里我们来介绍使用unset,array_splice删除数组中的元素区别吧 如果要在某个数组中删除 ...

随机推荐

  1. 实现bind函数

    面试中碰到的bind函数,今天来研究下 //1.bind的返回值是函数 var obj={ name:"zhouy" } function f() { console.log(th ...

  2. win10下配置多个mysql数据库

    mysql正常安装步骤:下载安装参考: 我配置的时8.0.13和5.7.27这两个版本: 配置完第一个数据库之后:复制ini文件给第二个数据库注意修改文件的端口时,先确认端口是否被占用 [mysql] ...

  3. PB系统颜色值

    Colour Red Green Blue 值黑色 Black 0 0 0 0白色 White 255 255 255 16777215灰色 Gray 192 192 192 12632256深灰色 ...

  4. php substr_count()函数 语法

    php substr_count()函数 语法 作用:统计一个字符串,在另一个字符串中出现次数大理石量具 语法:substr_count(string,substring,start,length) ...

  5. Yii 1.1 常规框架部署和配置

    <?php //index.php //入口文件配置操作 // change the following paths if necessary $yii=dirname(__FILE__).'/ ...

  6. spash和selenium浅析

    Splash是什么: Splash是一个Javascript渲染服务.它是一个实现了HTTP API的轻量级浏览器,Splash是用Python实现的,同时使用Twisted和QT.Twisted(Q ...

  7. 2017年度最具商业价值人工智能公司TOP50 榜单发布

    2017年度最具商业价值人工智能公司TOP50 榜单发布 未来最有赚钱潜力的50个人工智能项目都在这里了. 经过了60年的发展,人工智能在2017年,正式走向应用的元年. 从今年起,人工智能首次被写入 ...

  8. 生产环境下,oracle不同用户间的数据迁移。第一部分

    :任务名称:生产环境下schema ELON数据迁移至schema TIAN ######################################## 测试一:测试参数 数据泵数据导出:exp ...

  9. Java常用工具——java集合

    一.ArrayList package com.imooc.set; import java.util.ArrayList; import java.util.List; public class A ...

  10. 测开之路八十二:匿名函数:lambda表达式

    # 匿名函数:lambda表达式# lambda 参数: 逻辑f = lambda name: print(name)f('tom') f2 = lambda x, y: x + yprint(f2( ...