• Kotlin基础学习2


    写在前面

    在前文:Kotlin基础学习中简单学习了Kotlin的基本语法知识,但这些还没有涉及到Kotlin中十分重要的Lambda编程以及空指针校验等等特性。今天就把这部分写一写,但可能不会写的很好,毕竟我自己的对这方面的理解还是欠缺。关于java里的Lambda编程,我之前写过两篇博客一博客二,但写的不是很详细,需要的可以去看一看。

    集合的创建与遍历

    介绍

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

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

    List

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

    val list = ArrayList<String>()
    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)
    }
    

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

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

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

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

    与我们预期的一致。

    Set

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

    Map

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

    val map = HashMap<String,Int>()
    map.put("Apple",1)
    ...
    

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

    map["Apple"] = 1
    val 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})
      
    2. Kotlin规定,当Lambda参数是函数的最后一个参数时,可以把Lambda表达式放到括号外面:

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

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

      val maxLengthFruit = list.maxBy{fruit -> fruit.length}
      
    5. 最后,当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{
        override fun run(){
            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 {}
    

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

    总结

    本来想一篇博客将Lambda编程和空指针检查直接写完了的,但好像内容有点太多了,只写完了Lambda编程的部分。今天就先写到这里吧,关于空指针检查的部分明天再写。

  • 相关阅读:
    JavaScript之DOM查询
    JavaScript之this解析
    Qt之pro文件解析
    Qt5 调试之详细日志文件输出(qInstallMessageHandler)
    修改 Ubuntu的源为阿里源
    Unable to acquire the dpkg frontend lock
    gcc编译中文字符串后,windows控制台输出乱码
    stm32f103 time2配置,转载
    取反
    单片机,struct ,union定义标志,节约RAM
  • 原文地址:https://www.cnblogs.com/wushenjiang/p/13442617.html
Copyright © 2020-2023  润新知