Kotlin语言入门之-null安全

对于Java语言来讲,Kotlin对Null进行了一些处理,再具体的使用Kotlin中null异常好像不存在一样,一般我们几乎都不需要去担心它。在Java中null是一个不可跨越的思维,但是在kotlin我们却可以这么做:

这个代码可以被完美地编译(你可能会从IDE上得到一个警告),然后正常地执行,但是显然它会抛一个 NullPointerException 。这个相当不安全的。而且按照以前我们java思维,我们应该杜绝对null操作的出现。虽然Kotlin中,允许出现null及对其进行操作但是随着代码的增长,最终会得到 NullPointerException 或者丢失很多null检查(可能两者混合)。

究竟null是怎么被操作的呐?

大部分现代语言使用某些方法去解决了这个问题,Kotlin的方法跟别的相似的语言相比是相当另类和不同的。但是黄金准则还是一样:如果变量是可以是null,编译器强制我们去用某种方式去处理。指定一个变量是可null是通过在类型的最后增加一个问号。因为在Kotlin中一切都是对象(甚至是Java中原始数据类型),一切都是可null的。所以,当然我们可以有一个可null的integer:

一个可nul类型,你在没有进行检查之前你是不能直接使用它。这个代码不能被编译:

前一行代码标记为可null,然后编译器就会知道它,所以在你null检查之前你不能去使用它。还有一个特性是当我们检查了一个对象的可null性,之后这个对象就会自动转型成不可null类型,这就是Kotlin编译器的智能转换:

在 if 中, a 从 Int? 变成了 Int ,所以我们可以不需要再检查可null性而直接使用它。 if 代码之外,当然我们又得检查处理。这仅仅在变量当前不能被改变的时候才有效,因为否则这个value可能被另外的线程修改,这时前面的检查会返回false。 val 属性或者本地( val or var )变量。

上边的处理看上去有点繁琐,有人会问还不如以前对null进行相关的控制,话虽这样,但是在实际的工作中我们大多数时间是不需要null的,null并没有我们想象中的那么有用,当你想弄清楚一个变量是否可以为null时你就会发现这一点。话又说回来了,Kotlin也有它自己的使处理更简洁的方案。举个例子,我们如下简化代码:

这里我们使用了安全访问操作符( ? )。只有这个变量不是null的时候才会去执行前面的那行代码。否则,它不会做任何事情。并且我们甚至可以使用Elvis

operator( ?: ):

因为在Kotlin中 throw 和 return 都是表达式,他们可以用在Elvis operator操作符的右边:

然后,我们可能会遇到这种情景,我们确定我们是在用一个非null变量,但是他的类型却是可null的。我们可以使用 !! 操作符来强制编译器执行可null类型时跳过限制检查:

上面的代码将会被编译,但是很显然会奔溃。所以我们要确保只能在特定的情况下使用。通常我们可以自己选择作为解决方案。如果一份代码满篇都是 !! ,那就有股代码没有被正确处理的气味了。

可null性和Java库

在Java中,所有对象可以被定义为null。所以我们不得不处理大量潜在的在现实中不可能是null的null变量。这意味着我们的代码最后可能会有几百个 !! 操作符,这绝对不是一个好的主意。

当我们去处理Android SDK时,你可能看见所有Java方法的参数被标记为单个的 ! 。比如,Java中在一些获取对象的方法在Kotlin中显示返回 Any! 。这表示让开发者自己决定是否这个变量是否可null。很幸运,新版本的Android开始使用 @Nullable 和 @NonNull 注解来辨别参数是否可以是null或者否个函数是否可以返回null。当我们怀疑时,我们可以进入源码去检查是否会接收到一个null对象。我的猜想是在以后,编译器能够读取这些注解,然后强制(或者至少是建议)一个更好的方法。

现在开始,当一个Jetbrains的 @Nullable 注解(这个与Android的注解不同)被注解在一个非null的变量时,就会获得一个警告。相对的没有发生在 @NotNull 注解上。所以我们来举个例子,如果我们创建了一个Java的测试类:

然后在Kotlin中使用:

我们会发现,在 getObject 函数上会显示一个警告。但是这只是从现在才开始的编译器检查,并且它还不认识Android的注解,所以我们可能不得不花更多的时间来等待一个更智能的方式。不管怎么样,使用源码注解的方式和一些Androd SDK的知识,我们也很难犯错误。

比如重写 Activity 的 onCraete 函数,我们可以决定是否

让 savedInstanceState 可null:

这两种方法都会被编译,但是第二种是错误的,因为一个Activity很可能接收到一个null的bundle。只要小心一点点就足够了。当你有疑问时,你可以就用可null的对象然后处理掉用可能的null。记住,如果你使用了 !! ,可能是因为你确信对象不可能为null,如果是这样,请定义为非null。

这个灵活性在Java库中真的很有必要,而且随着编译器的进化,我们将可能看到更好的交互(可能是基于注解的),但是现在来说这个机制已经足够灵活了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181110G00IDG00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券