Kotlin基础学习3

写在前面

本文上接:Kotlin基础学习2,在之前学习了Koltin中的Lambda表达式后,今天来学习我感觉能够让Kotlin成为谷歌推荐的安卓推荐编程语言的原因——空指针检查。大伙儿在做安卓的时候,肯定能体会到NullPointerException的恐怖。遇到这种错误,总是不知道错在哪里。而Kotlin通过空指针检查基本解决了这一问题,今天就来学习一下空指针检查。

空指针检查

通常情况,我们在安卓开发中总是需要对某个控件或变量进行判空,以确保他不会造成空指针异常。但一旦项目的代码量上去了,总会有出错的时候。Kotlin通过在编译时对判空进行检查,虽然这么搞有时候会让代码很难写,但Kotlin还提供了很多的辅助工具方便处理。

可空类型系统

我们先来看一段代码:

fun doStudy(s:String){
    s.toUpperCase()
    s.toLowerCase()
}

这段看上去似乎有着空指针的风险,但实际上是没有的,因为Koltin默认所有的参数和变量都不可为空。所以这里传入的String参数一定不会为空,调用其中的方法自然没有任何问题。如果你尝试给他传入一个null参数,编译器会报错:

也就是说,Kotlin将空指针异常的检查提前到了编译时期,如果有空指针异常的风险直接不通过编译,更不要谈运行时报错了。这样就从根本上杜绝了空指针异常。

但你看到这里可能会觉得诧异,那我如果真的需要传入一个null值怎么办?Kotlin为我们提供了另外一套可为空的类型系统,即在变量类型后面加上问号即可,如:

fun doStudy(s:String?){
    s.toUpperCase()
    s.toLowerCase()
}

但这时候你会发现出现了红点:

很正常,因为此时我们的s是有可能为空的,必须排除他为空的可能,如下:

fun doStudy(s:String?){
    if(s != null){
		s.toUpperCase() 
         s.toLowerCase()
    }
}

这样就完事了,但我们发现,好像和之前用Java没什么区别啊?我不是还得写一堆的判空语句吗。别急,下面来学习Kotlin提供的辅助工具来更轻松的进行判空。

判空辅助工具

  • 首先来学习最常用的?.操作符,这个符号的意思很好理解——当对象不为空时调用相应的方法,当对象为空的时候就什么都不做,比如我们上面的判空语句:

    fun doStudy(s:String?){
        s?.toUpperCase()
        s?.toLowerCase()
    }
    

    还是很简单的吧?有了他我们就可以极大的简化我们的代码了。

  • 学习了?.后,再来学习一个也很常用的?:操作符,这个操作符左右都接收一个表达式,如果左边表达式的结果不为空则返回左边表达式的结果,否则就返回右边的结果,比如:

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

    这段代码的逻辑用?:操作符就可以简化成:

    val c = a ?: b
    

    当然,我们也可以将?.和?:一起用起来:

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

    这里使用了函数里单行代码的语法糖,并且结合了?.和?:。具体来看,当text为空时,就会返回0值,当text不为空时,就会返回它的长度length。

  • 当然,有时候Kotlin的编译器并不是那么智能。有的时候我们已经从逻辑上将空指针异常处理了,但Kotlin的编译器不知道,这时候还是会编译失败。比如以下这段代码:

    var content:String? = "hello"
    fun main(){
        if(content != null){
            printUpperCase()
        }
    }
    fun printUpperCase(){
        val upperCase = content.toUpperCase()
        println(upperCase)
    }
    

    这里我们定义了一个可为空的全局变量content,然后在主函数里对其进行了判空处理,当content不为空时才会调用下面的方法输出它的大写值。从逻辑上讲似乎没什么问题,但这段代码无法通过编译。因为printUpperCase()这个方法并不知道外部对content已经进行了判空处理,在调用toUpperCase()方法时还是会认为存在空指针异常。

    这种情况下,我们可以使用非空断言工具,写法是在对象的后面加上!!,如:

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

    断言,意思就是告诉编译器,我非常确信这里不会为空,你就不用管了。但这种工具如果自己不够确信的情况下,还是不要随意使用的。万一一个不好,就出了空指针异常。

  • 最后,学习一个函数let,通过这个函数的特性可以帮助我们简化空指针检查时的代码。let是一个函数,里面要传入一个Lambda表达式,且会把原始调用对象作为参数传递到Lambda表达式里。那么怎么用他来简化我们的代码呢?如下:

    fun doStudy(s:String?){
        s?.toUpperCase()
        s?.toLowerCase()
    }
    

    这是我们之前的代码,我们使用let函数:

    fun doStudy(s:String?){
        s?.let{s -> 
              s.toUpperCase()
              s.toLowerCase()
        }
    }
    

    再根据之前学习的Lambda表达式的特性,代码进一步简化:

    fun doStudy(s:String?){
        s?.let{
            it.toUpperCase()
            it.toLowerCase()
        }
    }
    

    要解释的话其实很简单,当对象不为空的时调用let函数,此时的s对象一定不为空,自然可以随便调用里面的方法了。

    let函数一般用在判断全局变量的时候。在对全局变量判空时,使用if并不能通过编译,因为全局变量的值随时可能被其他线程修改啊,即使做了判空处理也不能确保他没有空指针风险。

Kotlin中的小魔术

到这里,我们已经学习了很多Kotlin的基础知识了。最后,我把书上提到的Kotlin中的小魔术也记录一下,学会了这些也会提升我们的编码速度。

字符串内嵌表达式

一般,我们在Java中连接字符串都会使用+号,但这种方式太繁琐了,一不小心我们就会写错。但Kotlin支持使用字符串内嵌表达式来简化我们的操作。先来看语法规则:

"hello ${obj.name} nice to meet you"

可以看到,Kotlin允许我们在字符串中嵌入${}这种语法结构的表达式,并会自动替换这部分内容。如果学过web的话,会发现el表达式也是这样的写法。另外,当表达式中仅有一个变量时,还可以省略大括号:

"hello $name nice to meet you"

接下来我们直接在代码中使用呗,如下:

val brand = "Samsung"
val price = 1299.9
println("Cellphone(brand = $brand, price = $price)")

可以看到,无论是易读性还是易写性都上了一层楼,可以说十分方便了。

函数的参数默认值

如果我们学过C++的话,就会知道C++里是允许在函数上直接给变量一个默认值的。我们的Kotlin也是支持这种写法的。如:

fun printParams(num:Int,str:String = "hello"){
    println("num is $num, str is $str")
}

这里我们就给str指定了默认值hello,当我们调用这个方法时:

printParams(123)

不传入str的值,输出的结果也会如我们所愿的。

这时候我们换换,给num一个默认值:

fun printParams(num:Int = 100,str:String){
    println("num is $num, str is $str")
}

这时候我们怎么调用这个方法呢?像刚才那样的使用肯定是不行的,编译器会认为我们想把一个字符串赋值给第一个num参数,直接类型不匹配。这时候我们就可以使用键值对的写法了,如下:

printParams(str = "world")

这时候就无所谓先后顺序了,毕竟是按键值匹配的。

这里我们就提到最开始学习的次构造函数,我们说函数的参数默认值这个功能可以很大程度上替代次构造函数。为什么这么说呢?次构造函数的作用一般是什么呢?就是为类提供更多的赋值方式,方便调用。而我们直接在主构造函数上设置函数默认值能达到一样的效果,这时我们就可以使用任意传参组合了。

总结

仔细想想这一路的学习,会发现Kotlin借鉴了很多其他语言的优势,优化了Java的一些痛点。总的来说虽然Lambda表达式的使用和理解还是有些难度,但我想在日后更多的使用中会更加巩固这方面的知识吧。

posted @ 2020-08-06 19:46  武神酱丶  阅读(334)  评论(0编辑  收藏  举报