kotlin学习总结(三)

目录

五 Lambda编程

数组(Array)

集合

集合的函数式API

空指针检查

判空辅助工具

字符串内嵌表达式


五 Lambda编程

数组(Array)

使用arrayOf()来创建参数是一个可变参数的泛型对象。如下所示

val arr1= arrayOf(1,2,3,4,5)
val arr2= arrayOf("0","1","2",32.4f)

使用arrayOfNulls()用于创建一个指定数据类型且可以为空的给定元素的个数的数组。如下所示

val arr3 = arrayOfNulls<Int>(3)

除了以上方法,kotlin提供了专门的类来表示原始类型的数组。它们分别是

ByteArray字节型数组
ShortArray短整型数组
IntArray整形数组
LongArray长整型数组
BooleanArray布尔型数组
CharArray字符型数组
FloatArray浮点型数组
DobleArray双精度浮点型数组

kotlin中不支持字符串类型这种原始型数组。例子如下

val arr:IntArray= intArrayOf(1,2,3,4,5)

集合

List

val intList = listOf<Int>(1,2,3)//声明一个不可变的Int型List
val stringList= mutableListOf<String>("1","2","3")//声明一个可变的String的List

其中不可变的意思是该集合只能用于读取。

Set

val intList = setOf<Int>(1,2,3)//声明一个不可变的Int型Set
val stringList= mutableSetOf<String>("1","2","3")//声明一个可变的String的Set

Map 

val map = mapOf<String,Int>("1" to 1,"2" to 2,"3" to 3)//声明一个不可变的map
val mutableMap = mutableMapOf<String,Int>("1" to 1,"2" to 2,"3" to 3)//声明一个可变的map

在对Map进行操作的时候,推荐使用类似于数组下标的方式进行操作数据,如map["1"]=1

集合的函数式API

val list= listOf("Apple","Banana","Orange","Pear")
val maxLength= list.maxByOrNull { it.length }

上述代码表示找到集合中单词最长的元素。其中使用了集合的函数式API的写法。首先我们了解一下Lambda表达式的定义,其实它就是一小段可以作为参数传递的代码,它的语法结构为

{参数名1:参数类型,参数名2:参数类型 ->函数体}

最后一行代码会成为Lambda表达式的返回值。

于是刚刚那段代码的简化过程其实是这样的

var maxLength= list.maxByOrNull { fruit: String -> fruit.length }
maxLength=list.maxByOrNull { fruit->fruit.length }
maxLength=list.maxByOrNull { it.length }

  如果希望让所有的元素都变成大写,就可以使用如下API

val newList=list.map { it.uppercase(Locale.getDefault()) }

比较常用的API——filter函数,起到过滤元素的作用

val newList=list.filter { it.length<=5 }.map { it.uppercase(Locale.getDefault()) }

比较常用的API——any函数,用于判断集合中是否至少存在一个元素满足指定条件。

val anyResult=list.any { it.length<=5 }

比较常用的API——all函数,用于判断集合中是否所有元素都满足指定条件。

 val allResult=list.all { it.length<=5 }

上面我们讨论了kotlin中函数式API的用法,但实际上在kotlin调用Java方法的时候,也可以使用函数式API。具体来说,如果在kotlin代码中调用了一个Java方法,并且该方法接受一个Java单抽象方法接口参数,就可以使用函数式API。

Java原生API中有一个最为常见的单抽象方法接口参数——Runnable接口。这个接口中只有一个待实现的run()方法。

public interface Runnable {
    void run();
}

根据前面所说,对于任何Java方法,只要它接收Runnable参数,就可以使用函数式API,其实有很多方法接受了Runnable参数,但是Runnable主要还是结合线程一起使用的,因此这里我们通过线程类Thread来学习。Thread类的构造方法中接收了一个Runnable参数,可以使用如下代码创建一个子线程:

new Thread(new java.lang.Runnable() {
            @Override
            public void run() {
                System.out.println("Thread is running");
            }
        }).start();

这里使用了匿名类的写法,创建了一个Runnable接口的匿名类实例,并传入Thread类的构造方法,最后调用start方法执行这个线程。

但是如果采用kotlin来实现这段代码,只需要如下

Thread{
        println("Thread is running")
    }.start()

由于Runnable类中只有一个待实现方法,即使没有显式的重写run方法,kotlin也明白Runnable后面的Lambda表达式就是run方法中实现的内容。另外,如果Java方法参数列表中有且仅有一个Java单抽象方法接口参数,还可以把接口名省略,紧接着,如果Lambda表达式还是方法的唯一一个参数,还可以将方法的括号省略。

空指针检查

Java中的空指针异常只能通过程序员通过逻辑避免,在Kotlin中,它利用编译时判空检查的机制几乎杜绝了空指针异常。

Kotlin默认所有的参数和变量都不可为空,如果试图传入一个null参数,那么在编译时是不会通过的。但是如果我们的需求需要某个参数或者变量为空怎么办呢,Kotlin提供了一套可为空的类型系统,但是在使用可为空的类型系统的时候,我们需要在编译时间就将所有潜在的空指针异常处理掉,否则代码不会编译通过。

可空类型系统就是在类名后面加上问号,比如Int表示不可为空的整形,Int?表示可以为空的整形,String表示不可为空的字符串,而String?表示可为空的字符串,

fun doStudy(study:Study?){
    if (study != null) {
        study.doHomework()
        study.read()
    }
}

如果希望传入的参数可以为空,那么将参数类型后面加上?,然后再对参数进行判空逻辑操作,即可避免空指针异常。但是为了在编译时期处理掉所有的空指针异常,我们需要编写很多检查代码,如果每一处都使用if判断语句的话,则会让代码比较啰嗦,而且if还处理不了全局变量的判空问题。

为此,Kotlin提供了一系列的辅助工具。

判空辅助工具

首先是?.操作符,表示当对象不为空的时候正常调用相应方法,当对象为空的时候什么都不做,

比如上一段代码可以写成如下样子

fun doStudy(study:Study?){
        study?.doHomework()
        study?.read()
}

还有?:操作符,这个操作符左右两边都接收一个表达式,如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。如果要编写一个函数来获得一段文本的长度,传统的写法如下

fun getTextLength(text:String?):Int{
    if (text != null) {
        return text.length
    }
    return 0
}

使用?:操作符简化如下

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

由于text是可能为空的,因此我们在调用length字段的时候要使用?.操作符,当text为空的时候,text?.length返回一个null,然后左边为空返回右边。

不过Kotlin的空指针检查也不总是那么智能,比如如下代码

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

这段代码是不能通过编译的,因为在printUpperCase方法总认为存在空指针异常,但是事实上我们在main函数中已经做了非空检查。如果需要强行通过编译,则可以使用!!操作符,如下所示,

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

但是这是一种有风险的写法,如果有更好的实现方式,最好不要使用它。

最后还有一个比较与众不同的辅助工具——let,let既不是操作符,也不是关键字,而是一个函数,这个函数提供了函数式API的编程接口,并将调用对象作为参数传递进Lambda表达式,示例如下

obj.let{obj2->
    //
}

事实上obj2就是obj。

let函数的特性如果搭配?.操作符可以在空指针检查时起到很大作用,上文中?.操作符还可以进一步优化成下面这个样子

fun doStudy(study:Study?){
    study?.let {stu->
        stu.doHomework()
        stu.read()
    }
}

当study不为空的时候,调用let函数,把自己作为参数传递进去执行逻辑,此时对象肯定不为空。当Lambda参数列表只有一个参数时,代码可以进一步简化

fun doStudy(study:Study?){
    study?.let {
        it.doHomework()
        it.read()
    }
}

除此之外,let函数可以处理全局变量的判空问题,而if语句无法做到。

字符串内嵌表达式

Java是并不支持字符串内嵌的,相反的,Kotlin实现了这一功能。如下

fun getName(){
    val name="Jack"
    println("name is ${name}")
}

这种方式使得我们不用在去拼接字符串,一定程度上增加了开发的便利性。特别的,如果表达式中只有一个变量,可以将大括号省略,如下

fun getName(){
    val name="Jack"
    println("name is $name")
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值