前言
在上一篇中,主要是了解性的初探了Kotlin,发现了与Java还是有明显的区别的。在这一篇中将会重点以Kotlin对应的Null与异常进行详解。
1. Null类型
如图所示
在我们定义变量的时候,如果没有赋初始值,那么编译器就会直接提示语法错误!当你准备直接赋初始值为null的时候,又会提醒你Show usages of variable 'name'
。
其实这个是Kotlin的特性之一。就是为了避免出现非空而设定的。毕竟变量的类型要根据变量的属性值而确定,属性值为空,它就自然不知道是什么类型的变量。
那问题来了,如果说这个变量我能确定它的类型,但是它因逻辑原因属性值就是可能会空怎么办?
fun main() {
var str:String?= null //这里就不能用val了哈
//str="hello word"
println("input: ${str?.capitalize()}")
}
当然Kotlin肯定会想到这样的问题!于是乎就有了新的语法var 变量名:变量类型?
用来定义可空变量。
当然你变量既然可能为空了,为了避免报空指针异常,Kotlin也给我们准备好了变量为空的解决方案,语法为:可空变量?.对应的属性方法
,这里的意思就是,当可空变量为空时,将会自动忽略后面的方法。用java的意思就是
if(str==null){//只有变量为null的时候,才会return
return;
}
//当str对应长度为0时,还是会执行下面方法
str.capitalize();
虽然在这字符串长度为0能够运行下面的方法,但是如果想在这种情况想有特殊处理该怎么办呢?
1.1 let关键字
fun main{
val str: String? = ""?.let {
//如果此字符序列不为空并且包含除空白字符以外的某些字符,则返回 true。
if (it.isNotBlank()) {
it.capitalize()
} else{
//此处可放为空的逻辑处理
"abc"
}
}
println(str)
}
先看运行效果
abc
从这个运行效果可知,如果说字符串长度为0,那么将会返回默认指定的字符串。
在Kotlin里使用一个语法,不仅仅要做到会用,还要知其原理,要知道它为什么这样写!只有这样当你面对Kotlin高级API时,依然能够从容解读并使用对应的API!
现在我们进入let
源码看看它是怎么实现的!
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
对于不懂Kotlin的你,可能第一眼看到这个就会觉得这个let
有点复杂。但只要懂得如何拆分解析它那就能轻易理解:
- 首先看
fun后面的 <T, R>
,你阔以理解成在java里声明对应的泛型,并且声明了两个 - 接着看
T.let(block: (T) -> R): R
,先将block: (T) -> R
忽略掉,可得:T.let(): R
,可以将它看成T对象里含有let方法,方法的返回值为:R
- 这里再结合上面那个例子
""?.let
,因为这里.let
前是字符串,所以这个T
暂时可以理解为字符串类型 - 于是乎
T.let(block: (T) -> R): R
就变成了String.let(block: (String) -> R): R
- 接着继续看
block: (String) -> R
,而这个依然是一个方法,形参为字符串,返回值为R; - 最后我们看
return block(this)
,就可以得出,在let
里面调用了block
方法,并且将block
方法的返回值当做闭包let
的返回值!
注意:这6项一定要分析透彻,这关系到后面你能不能学好kotlin!如有不明白的小伙伴可以留言,我会依次解答。
结合上面的分析,再看看下面的代码。
fun main{
var content:Int="asd".let {
println("当前value为:$it")
123 //外部确认了是Int类型,所以这里写非int类型的返回值直接语法提示错误!
}
var content1:Double=123.let{
println("当前value为:$it")
1.2//外部确认了是Double类型,所以这里写非Double类型的返回值直接语法提示错误!
}
var content3=1.256.let {
println("当前value为:$it")
"我是content3" //这里可放任意类型的返回值
}
println(content3.javaClass.name)
}
运行效果
当前value为:asd
当前value为:123
当前value为:1.256
java.lang.String
这里的content与content1变量类型都是确定的,只有变量content3不确定,但是它的类型由let
闭包返回值类型而确定。
到这里就能完全明白let
里面的T和R
,分别代表对应的使用者以及闭包里面it
的变量类型,和闭包的返回值类型以及外部接收该闭包变量的变量类型。
1.2 !!符号
这个很简单,意思就是如果为空的话,直接运行报错。
fun main{
var str:String?= null!!.capitalize()
}
运行效果
Exception in thread "main" kotlin.KotlinNullPointerException
at KotlinStudy02Kt.main(KotlinStudy02.kt:36)
at KotlinStudy02Kt.main(KotlinStudy02.kt)
运行效果直接报错。
1.3 orEmpty方法
这个方法意思就是当目标为null
的时候,通过这个方法将会返回长度为0的空字符。
fun main{
var str: String? = null.orEmpty()
if (str!=null){
str=str.capitalize()
}else{
println("str 为Null")
}
str= str?.capitalize().plus(" is Great. length: ${str?.length}")
println(str)
val strWithSafe: String = str ?: "butterfly"
println(strWithSafe.length)
}
运行效果
is Great. length: 0
20
1.4 三目表达式
fun main{
var str: String? = null
val strWithSafe: String = str ?: "butterfly"
//这里用java表示为 String strWithSafe=(str==null?"butterfly":str)
println(strWithSafe)
}
fun main{
var str: String? = null
str = str?.let { it.capitalize() } ?: "butterfly"
println(str)
}
这些都很简单,直接可以一笔带过,看看运行效果
butterfly
2. 自定义异常
fun main{
var number: Int? = null
checkOperation1(number)
}
fun checkOperation1(number: Int?) {
number ?: throw MyTestException()
}
class MyTestException : Exception("这是自定义异常")
}
运行效果
Exception in thread "main" MyTestException: 这是自定义异常
at KotlinStudy02Kt.checkOperation1(KotlinStudy02.kt:64)
at KotlinStudy02Kt.main(KotlinStudy02.kt:58)
at KotlinStudy02Kt.main(KotlinStudy02.kt)
当然你也阔以用自带的
var number: Int? = null
number = checkNotNull(number, { "操作不合法" })
运行效果
Exception in thread "main" java.lang.IllegalStateException: 操作不合法
at KotlinStudy02Kt.main(KotlinStudy02.kt:59)
at KotlinStudy02Kt.main(KotlinStudy02.kt)
结束语
好了,到这相信你对Kotlin对应的null处理、let关键字、自定义异常有了一定的认知。在下一篇中,将会重点以Kotlin对应的字符串操作、数字类型、标准库函数进行详解。