Kotlin基础学习(二)

在写这篇文章之前,Kotlin的基础和进阶课程都已经学习完了。这里简单做一个总结:

  • Kotlin基础学习(一)主要知识点:Kotlin中的变量与函数,逻辑控制,类与对象

  • Kotlin基础学习(二)主要知识点:集合的创建与遍历,Lambda编程

  • Kotlin基础学习(三)主要知识点:空指针检查,Kotlin中的简单特性

  • Kotlin进阶学习(四) 主要知识点:标准函数,静态方法,变态延迟初始化与密封类

  • Kotlin进阶学习(五)主要知识点:扩展函数,运算符重载,高阶函数,内联函数

  • Kotlin进阶学习(六)主要知识点:高阶函数的应用,泛型基础,infix函数

  • Kotlin进阶学习(七)主要知识点:泛型高级,委托

  • Kotlin进阶学习(八)主要知识点:协程的内容

集合的创建与遍历

介绍

要学习Lambda编程,集合的函数式API接口是入门Lambda的最佳案例,不过我们要先学习Kotlin中的集合。

集合,对于熟悉java的人来说,不用多说了。传统意义上的集合主要指List和Set,如果广泛点说的话Map这样的键值对结构也可以说是集合。在java里,List,Set,Map都是接口,我们一般都用他们的实现类,如ArrayList,HashSet,HashMap等等。

List

现在,为了入门集合,我们先提出这样一个需求:创建一个包含多个水果名称的集合。放到java里,相信我们都会写,kotlin中自然也能这么写:

val list = ArrayList()
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Grape")

但这种写法未免太过于繁琐了。Kotlin为此提供了一个内置的listOf()方法来简化这种初始化的写法:

val list = listOf("Apple", "Banana", "Orange", "Pear","Grape")

可以看到,我们只用了一行代码就完成了集合的初始化操作。

之前我们学习了for-in循环,for-in循环不仅可以遍历区间,也可以遍历集合,如下:

for (fruit in list) {
    println(fruit)
}

我们输出,发现输出了我们想要的结果:

Kotlin基础学习(二)_第1张图片

不过这里需要注意,listOf()函数创建的是一个不可变的集合,什么叫不可变的集合呢?其实就是只能读,不能增删改。我们从这里也可以看出kotlin对于这些不可变性控制的十分严格。那我们怎么创建一个可变的集合呢?使用mutableListOf()函数就可以了:

val list = mutableListOf("Apple", "Banana", "Orange", "Pear","Grape")
list.add("WaterMelon")
for (fruit in list) {
	println(fruit
}

这里我们添加了一个新的元素西瓜,我们运行看看结果:

Kotlin基础学习(二)_第2张图片

与我们预期的一致。

Set

而对于Set,其实和List的用法是几乎一模一样的,只是将用的函数变成了setOf()和mutableSetOf()而已,这里就不再赘述了。

Map

我们在java中要创建一个Map要怎么做呢?很简单,new一个HashMap,定义好键和值的类型,然后挨个put就是了,在kotlin中也是一样的:

val map = HashMap()
map.put("Apple",1)
...

但其实在kotlin中不建议我们使用put()和get()方法来对Map进行添加和读取操作。而是更加推荐一种类似数组下标的语法结构,如下:

map["Apple"] = 1val number = map["Apple"]

如果学过php的话,会发现与php中的关联数组的用法似乎很类似呢。

当然,这种写法仍然很麻烦。那么kotlin中有我们上面用的listOf或者setOf()方法吗?显然是有的:

val map2 = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3)

这里看上去是用to关键字进行关联的,但实际上to并不是一个关键字,而是一个infix函数。这里暂时不再细究了。

最后,我们要遍历一个Map集合呢?当然也可以使用for-in了,如下:

val map2 = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3)
    for ((fruit,number) in map2) {
        println("fruit is " + fruit + ", number is " + number)
    }

这里与刚才的区别很明显,我们直接将键和值分别取出来,赋值给了fruit和number,最后进行了一个打印输出。如果学过ES6的语法的话,会发现也很类似。看来Kotlin吸取了很多脚本语言的特性啊。

Lambda编程——集合的函数式API

入门介绍

想也知道,函数式API多的一批。这里我们就简单了解一些简单的函数式API,主要是为了学习Lambda表达式的语法结构。

我们先提出一个需求:如何在一个水果集合里面找到单词最长的那个水果呢?要实现这个需求我们很自然的会想到这样的一种写法:对list集合进行遍历,使用if语句挑选出长度最长的那个水果单词即可。这段代码我就不在这里写了,相信各位都能十分容易地写出这样的代码。下面我们来看看使用kotlin的函数式编程API写法怎么写呢?如下:

val list = listOf("Apple", "Banana", "Orange", "Pear","Grape")
val maxLengthFruit = list.maxBy { it.length }

可以看到,我们一行代码就找到了最长的那个水果单词,但可能还无法理解这段代码。下面我们来一点点的看。

Lambda编程的标准写法

Lambda编程的定义,就是一小段可以作为参数传递的代码,这个一小段代码指的是什么呢?kotlin并没有限制,但我们使用时最好不要写太长的代码,毕竟是参数嘛。

那么我们直接来看Lambda表达式的语法结构:

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

首先,最外层是一对大括号,如果有参数传入到Lambda表达式的话,我们就需要声明参数列表,在参数列表的尾部加一个-> 然后在函数体里写我们需要写的代码就好了。最后一行代码会自动作为Lambda表达式的返回值

当然,我们很多时候不会写这么标准的语法结构,大部分时候我们都使用的简化形式。下面我们一步一步把代码简化到入门介绍里的那一行代码。

Lambda编程的简化

仔细观察入门介绍里的一行代码:

val maxLengthFruit = list.maxBy { it.length }

这里使用了一个maxBy函数,maxBy函数其实就是一个普通的函数,但需要传入一个lambda表达式。maxBy的工作原理就是根据我们传入的条件来遍历集合,然后找到最大值。比如我们想找单词最长的值,传入单词的长度即可。

理解了maxBy函数后,我们就可以按照上面的格式写一个标准的Lambda表达式:

val list = listOf("Apple", "Banana", "Orange", "Pear","Grape")
val lambda = {fruit:String -> fruit.length}
val maxLengthFruit = list.maxBy(lambda)

这段代码很好懂吧?我们定义了一个fruit变量,然后把他的长度返回到了lambda变量,将其作为参数传给了maxBy函数。接下来我们开始简化。

  1. 显而易见的,我们并不需要单独定义一个lambda变量:

val maxLengthFruit = list.maxBy({fruit:String -> fruit.length})
  1. Kotlin规定,当Lambda参数是函数的最后一个参数时,可以把Lambda表达式放到括号外面:

val maxLengthFruit = list.maxBy(){fruit:String -> fruit.length}
  1. 接下来,如果Lambda参数是函数的唯一一个参数的话,还可以省略括号:

val maxLengthFruit = list.maxBy{fruit:String -> fruit.length}
  1. 再然后,我们知道kotlin有优秀的类型推导机制,这就使得我们的Lambda表达式的参数列表大多数情况下不必声明参数类型:

val maxLengthFruit = list.maxBy{fruit -> fruit.length}
  1. 最后,当Lambda表达式只有一个参数时,也不必声明参数名,直接用it关键字来代替即可:

val maxLengthFruit = list.maxBy{it.length}

可以看到,我们一步一步简化到了入门介绍里的一行代码。

常用的函数式API

学习完了Lambda表达式,我们就学几个最常用的函数式API来巩固一下吧。

首先来学习map函数。集合中的map函数是一种最常用的函数式API,可以把集合中的元素映射成另外的值,最终形成新的集合。比如我们希望将所有的水果名变成大写:

val list = listOf("Apple", "Banana", "Orange", "Pear","Grape")
val newList = list.map { it.toUpperCase() }

map函数十分强大,可以做很多事。接下来我们学习另一个函数式API——filter函数,filter函数听名字就知道了,是用来过滤集合中的元素的,可以单独使用也可以配合map函数。

比如我想保留五个5个字母以内的水果,且都变成大写:

val list = listOf("Apple", "Banana", "Orange", "Pear","Grape")
val newList = list.filter { it.length <= 5 }
				.map { it.toUpperCase() }

最后,我们学习一下any和all函数,any用于判断集合中是否至少存在一个元素满足条件,all表示判断集合中是否所有元素都满足条件:

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

其中的list指上文的list。返回值是布尔类型的true或false。

Lambda编程——Java函数式API的使用

介绍

刚才我们学习了很多,但好像和安卓开发都没有什么关系。接下来我们学习一下Java函数式API的使用,学习了这个特性后会极大的便利我们的安卓开发。

kotlin实际上也可以使用函数式API,但有一定的条件限制:如果我们在Koltin中调用了一个java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口指的是只有一个待实现的方法。

从Runnable接口入门

听起来似乎很抽象,接下来我们还是通过代码学习:

Thread(object:Runnable{
    overridefunrun(){
        println("Thread is running")
    }
}).start()

由于Kotlin中没有new关键字,固创建匿名内部类就不能再使用new了,改用了object关键字。这样写虽然不是很复杂,但好像跟java的匿名类写法没啥区别。

但Thread类的构造方法此时就符合Java函数式API的使用条件,我们就直接对代码进行精简:

Thread(Runnable{
    println("Thread is running")
})

可以看到,已经很方便了。但这里提出一个特性——如果一个Java方法的参数列表不存在一个以上Java单抽象方法接口参数,就可以将接口名进行省略:

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

这样,代码就十分简单了。可这和我们做安卓开发有什么关系呢?要知道Android SDK里面还是使用Java编写的,有很多接口都具有这种特性。比如我们最常用的按钮的点击事件:

button1.setOnClickListener {}

就可以直接这么写了,还是方便了很多的。

你可能感兴趣的:(Android,kotlin,android,基础语法,迁移学习)