NullPointException 利器 Kotlin 可选型
2017-05-31 20:36
218 查看
NullPointException (简称 NPE ) 被称作
The Billion Dollar Mistake 一直困扰着Java 和 Android 开发者。Kotlin 的类型系统中提供可选类型用于减少 NPE 问题带来的风险。
虽然,Kotlin 提供了可选类型用于减少 NPE 问题的风险,但是并没有办法完全消除 NPE 带来的隐患,本问将探讨如何巧妙地使用「可选型」更好的规避 NPE 的发生。
因此,如果你后面任何时候使用该变量时,都可以放心的使用而不用担心会发生 NPE。所以要想远离 NPE,首先需要「尽可能的使用非空类型的定义」。
在使用可选型变量的时候,这个变量就有可能为空,所以在使用前我们应该对其进行空判断(在 Java 中我们经常这样做),这样往往带来带来大量的工作,这些空判断代码本身没有什么实际意义,并且让代码的可读性和简洁性带来了巨大的挑战。在网上可以看到许多人针对如何减少 NPE 提出了自己的建议,有的的确很不错,但成本依然很大。除此之外,还有一个最可恶的场景「我们会忘记」。
Kotlin 为了解决这个问题,它并不允许我们直接使用一个可选型的变量去调用方法或者属性。
你可以和 Java 中一样,在使用变量之前先进行空判断,然后再去调用。如果使用这种方法,那么空判断是必须的。
注意: 如果你定义的变量是全局变量,即使你做了空判断,依然不能使用变量去调用方法或者属性。这个时候你需要考虑使用下面的介绍的方法。
Kotlin 为可选型提供了一个安全调用操作符
这里
Kotlin 还提供了一个强转的操作符
上面有提到一种情况,当
其实你还可以在
上面代码的只会输出
需要注意的是,这个方法调用的时候必须要使用
参考这个函数的实现,下面我尝试提供几个自己定义的方法。
你可以处理一些逻辑以后,再返回一个可用的值。
也可以处理一些逻辑后, 通过
由于没有编译器的支持,所以暂时还不能实现
空屏蔽。
这里定义的两个函数的实现,你可以自己尝试去实现一下,就当是个练习(鬼笑)。AndroidExtension有具体的实现代码。
尽可能的使用非空类型的定义
远离
熟练使用
自定义一些常用的函数,让自己的代码更流畅
trailing-closures-in-guard
The Billion Dollar Mistake 一直困扰着Java 和 Android 开发者。Kotlin 的类型系统中提供可选类型用于减少 NPE 问题带来的风险。
虽然,Kotlin 提供了可选类型用于减少 NPE 问题的风险,但是并没有办法完全消除 NPE 带来的隐患,本问将探讨如何巧妙地使用「可选型」更好的规避 NPE 的发生。
可选型定义
非空类型
我们先从可选型的定义开始,当我们在 Kotlin 中定义一个变量时,默认就是非空类型的,当你将一个非空类型置空的时候,编译器会告诉你这不可行。var a: String = "abc" a = null // compilation error
因此,如果你后面任何时候使用该变量时,都可以放心的使用而不用担心会发生 NPE。所以要想远离 NPE,首先需要「尽可能的使用非空类型的定义」。
可选型(可空类型)
虽然「非空类型」能够有效避免 NPE 的问题,但是有时候我们总不可避免的需要使用「可选类型」。在定义可选型的时候,我们只要在非空类型的后面添加一个?就可以了。
var b: String? = "abc" b = null // ok
在使用可选型变量的时候,这个变量就有可能为空,所以在使用前我们应该对其进行空判断(在 Java 中我们经常这样做),这样往往带来带来大量的工作,这些空判断代码本身没有什么实际意义,并且让代码的可读性和简洁性带来了巨大的挑战。在网上可以看到许多人针对如何减少 NPE 提出了自己的建议,有的的确很不错,但成本依然很大。除此之外,还有一个最可恶的场景「我们会忘记」。
Kotlin 为了解决这个问题,它并不允许我们直接使用一个可选型的变量去调用方法或者属性。
val l = b.length // compilation error
你可以和 Java 中一样,在使用变量之前先进行空判断,然后再去调用。如果使用这种方法,那么空判断是必须的。
val l = if (b != null) b.length else -1
注意: 如果你定义的变量是全局变量,即使你做了空判断,依然不能使用变量去调用方法或者属性。这个时候你需要考虑使用下面的介绍的方法。
Kotlin 为可选型提供了一个安全调用操作符
?.,使用该操作符可以方便调用可选型的方法或者属性。
val l = b?.length
这里
l得到的返回依然是一个可选型
Int?。
Kotlin 还提供了一个强转的操作符
!!,这个操作符能够强行调用变量的方法或者属性,而不管这个变量是否为空,如果这个时候该变量为空时,那么就会发生 NPE。所以如果不想继续陷入 NPE 的困境无法自拔,请不要该操作符走的太近。
Elvis
操作符
上面有提到一种情况,当 b为空时,返回它的长度值给一个默认值 -1。要实现这样的逻辑当然可以用
ifelse的逻辑判断实现,但 Kotlin 提供了一个更优雅的书写方式
?:。
val l = b?.length ?: -1
b?.length ?: -1和
if (b != null) b.length else -1完全等价的。
其实你还可以在
?:后面添加任何表达式,比如你可以在后面会用
return和
throw(在 Kotlin 中它们都是表达式)。
fun foo(node: Node): String? { val parent = node.getParent() ?: return null val name = node.getName() ?: throw IllegalArgumentException("name expected") // ... }
let
函数
let是官方
stdlib提供的标准函数库里面的函数,这个函数巧妙的利用的 Kotlin 语言的特性让
let接受的表达式参数中的调用方是非空的。
val listWithNulls: List<String?> = listOf("A", null) for (item in listWithNulls) { item?.let { println(it) } // prints A and ignores null }
上面代码的只会输出
A,而不会输出
null。
需要注意的是,这个方法调用的时候必须要使用
?.操作符调用才能生效哦。如果你的部分代码依赖于一个可选型变量为非空的时候,就可以使用
let函数。
参考这个函数的实现,下面我尝试提供几个自己定义的方法。
自定义处理
这里定义的两个方法是参考Swift里面的
if let和
guard进行的抽象。
orElse
函数
orElse是和
Elvis函数结合使用的,默认
Elvis后面只能直接或者执行一个表达式获取返回值或者直接通过
return或者
throw结束当前函数的执行。结合
orElse函数,你能够更加灵活的处理前面的
null。
你可以处理一些逻辑以后,再返回一个可用的值。
var a:String? = null var b = a ?: orElse { // 做任何事 return@orElse "s" }
也可以处理一些逻辑后, 通过
return或者
throw结束当前函数的执行。
var a:String? = null var b = a ?: orElse { // 做任何事 return }
guard
函数
Elvis默认只能对单个变量或表达式是否为空进行处理,当碰到多个变量需要一起判断时,就会束手无策,
guard就是为了解决这个问题。
fun testGuard(a: String?, b: String?, c: String?){ guard(a, b, c) ?: orElse { print("a or b or c is null ") return } // 现在 `a`,`b`,`c` 都是不为空 }
由于没有编译器的支持,所以暂时还不能实现
空屏蔽。
这里定义的两个函数的实现,你可以自己尝试去实现一下,就当是个练习(鬼笑)。AndroidExtension有具体的实现代码。
总结
经过一系列分析以后,我们已经对怎么使用好 Kotlin 可选型有一定的了解,如果不想 NPE 问题不断困扰,可以参考这里总结的几条。尽可能的使用非空类型的定义
远离
!!,如果非要用,请调用代码在前面「三行之内」进行非空判断
熟练使用
Elvis操作符
自定义一些常用的函数,让自己的代码更流畅
参考资料
null-safetytrailing-closures-in-guard
相关文章推荐
- 避免NullPointException
- Android中使用ViewGroup.removeViews()时出现NullPointException解决方案
- Android:使用ButterKnife注解绑定控件后报NullPointException
- class.newInstance()报NullPointException问题解决
- android application中存储全局变量导致NullPointException
- PrintWriter类的write方法抛出NullPointException
- 【Android 排错第一例】NullPointException
- 用volley出现NullPointException
- spring使用体会——sesionFactory注入的空指针异常(nullpointException)
- android错误-nullpointException的原因
- 解决空指针异常NullPointException
- 启动Eclipse时报错:控制台NullPointException
- Spring 使用注解注入,调用类实例出现 java.lang.NullPointException 问题的解决方法
- ButterKnife 从7.x升级到8.x带来的NullPointException
- kotlin与Spring, 默认类,方法,property为final带来的问题--依赖注入失效,NullPointerException异常
- Java.NullPointException 之 Service(业务层) 空 调DAOImpl实例方法,执行报错问题
- pig 使用tez引擎 union操作符 NullPointException
- OutputStream类执行write函数时出现NullPointException的处理
- myEclipse刚打开启动报Errors running builder 'DeploymentBuilder' on project '工程名' xxxNullpointException 的错误
- 厌倦了NullPointException?Optional拯救你