美文网首页
kotlin 学习教程(五) |函数和函数式编程

kotlin 学习教程(五) |函数和函数式编程

作者: 西小瓜 | 来源:发表于2020-02-17 11:46 被阅读0次

    1. 前言

    我们知道,在程序中,通常情况下。一个类会有自己的方法(函数)以及属性,这些方法代表了该类的特性或者说具有的能力。今天我们一起来研究一下 kotlin中的函数。

    2.如何声明一个函数

    在 kotlin 中,我们通过关键字 fun 来声明一个函数

    fun multiply(x:Int,y:Int):Int{
       ...  //代码块
        return x*y
    }
    

    如上面的代码所示:我们定义了函数multiply()并指定其返回类型为 Int 类型。

    3.Lambda表达式

    3.1 Lambda表达式介绍

    从Java8 开始,Lambda表达式在 Lambda表达式,在其他的编程语言中(例如:Scala语言),这种表达式是语法糖中的一种,在 kotlin 中,也支持这种语法,它允许把函数作为一个方法的参数,可以使代码变的更加简洁紧凑。

    Lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。但是我们在用的时候不必关心起底层实现。不过Lambda的出现确实是减少了代码量的编写,同时也是代码变得更加简洁明了。

    3.2 Java 8 Lambda 表达式
    3.2.1 语法:
    (parameters) -> expression
    或
    (parameters) ->{ statements; }
    
    3.2.2 特征
    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

    • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

    3.2.3 代码如下:
    // 1. 不需要参数,返回值为 5  
    () -> 5  
      
    // 2. 接收一个参数(数字类型),返回其2倍的值  
    x -> 2 * x  
      
    // 3. 接受2个参数(数字),并返回他们的差值  
    (x, y) -> x – y  
      
    // 4. 接收2个int型整数,返回他们的和  
    (int x, int y) -> x + y  
      
    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
    (String s) -> System.out.print(s)
    

    从上面可以看出,代码简洁了不少。

    我们再来看一个例子,在java中假如要计算两个数的和,则代码如下:

    import java.util.function.BiFunction;
    
    public class helloword {
        public static void main(String[] args) {
            BiFunction<Integer,Integer,Integer> sum=(Integer x, Integer y) ->{
                return x+y;
            };
            System.out.println("sum(3, 4) = " + sum.apply(3, 4));
        }
    
    }
    

    运行结果如下:


    Snipaste_2020-02-17_10-31-55.png

    但是,在 java 8 以下版本,并不支持 Lambda 表达式,而这个时候.kotlin 完美的兼容了 java 8 以下版本对 lamdba 表达式的支持,并且能够进行混合开发。

    3.3 kotlin Lamdba 表达式

    好,我们再来看看 Android 中 kotlin 的使用,Kotlin语言中

    3.3.1 Lamdab 表达式 的声明:

    lambda表达式的完整语法如下:

    { params -> expressions }
    
    • params表示参数列表,expressions表示具体实现,可以是单行语句,也可以是多行语句。

    • Lamdba 表达式的值为大括号最后一行的值。

    3.3,2 Lamdba 表达式的类型表示:
    • () ->unit 无参,返回值为Unit

    • (Int)-> Int 传入整型,返回一个整型

    • (String,(String)->String)->Boolean 传入字符串,Lamdba 表达式,返回 Boolean

    3.3.3 Lamdba 表达式的调用:
    • 使用 () 进行调用,相当于invoke()
    3.3.4 Lamdba 表达式的简化:
    args.forEach(){ println(it) }  // 如果函数的最后一个参数是lamdba 表达式,则可以将lamdba 放在括号外面
    args.forEach{ println(it) }  // 如果函数参数只有一个lamdba表达式,则调用时小括号可以省略。
    args.forEach(::print)    //入参,返回值与形参一致的函数可以用函数引用的方式作为实参传入。
    

    上述同样的功能,kotlin 语言的实现如下:

    • 自定义函数来实现:
    fun main(args: Array<String>) {
      val result=sum(3,4)
        println(result)
    }
      fun sum(arg1:Int,arg2:Int)=arg1+arg2
    
    • 使用 Lamdba 表达式实现:
    fun main(args: Array<String>) {
        println(result(3, 4))
    }
    
    val result = { arg1: Int, arg2: Int ->
        println("$arg1+$arg2=${arg1 + arg2}")
        arg1 + arg2
    }
    

    我们再来看个例子,要求:遍历kotlin 中main函数的参数,

    • 示例代码:Android 中最常见的点击事件
    tv_toLogin.setOnClickListener(object:View.OnClickListener{
                override fun onClick(v: View?) {
                    Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() 
                }
    
            })
    
    • 使用 Lambda 表达式的点击事件
    tv_toLogin.setOnClickListener({
                Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() 
            })
    

    怎么样?有没有感觉 lamdba 表达式使用起来既简洁又优雅。

    4. 高阶函数

    4.1 什么是高阶函数

    高阶函数就是以另一个函数作为参数或返回值的函数,Kotlin 可以以 lambda 或参数引用作为参数或返回值,所以,任何以 lambda 或函数引用作为参数或返回值的都是高阶函数。

    4.2 常见的高阶函数
    4.2.1 forEach()函数
    fun main(args: Array<String>) {
       val list= listOf(1,2,3,4,5,6,7,8)
        val newList=ArrayList<Int>()
        list.forEach{
            val newElement=it *2+3
            newList.add(newElement)
        }
        newList.forEach(::println)
    }
    

    运行结果如下:

    Snipaste_2020-02-17_10-31-55.png
    4.2.2 :map()函数

    map: 接受一个lambda表达式,并且有返回值,形成一个新的list,实现对集合中的元素进行修改

    好,我们通过高阶函数 map 来实现如上的效果:

    fun main(args: Array<String>) {
       val list= listOf(1,2,3,4,5,6,7,8)
       val newList=list.map {
           it*2+3
       }
        println(newList)
    }
    

    运行结果如下:

    Snipaste_2020-02-17_10-31-55.png

    以上代码中通过高阶函数 map 实现了对集合中每个元素乘以2再加3的操作,不用去遍历集合中每个元素,是不是简单了许多。

    4.2.3 flatMap()函数

    flatMap是map和flat两个函数的“复合逻辑,可以将集合中的数据进行合并成一个集合。

    示例代码如下:

    fun main(args: Array<String>) {
      val list= listOf(
          1..20,
          21..30,
          31..100
      )
      
       val flatList=list.flatMap {
           it.map {
               "NO.$it"
           }
       }
    
        flatList.forEach{
            println(it)
        }
    }
    

    运行结果如下: 将集合中数字从1~100打印输出,每个数字前标有“NO.”


    Snipaste_2020-02-17_10-31-55.png
    4.2.4 filter()函数

    传入Lambda 表达式为 true 是,保留该元素;使用filter对集合进行按条件过滤

    fun main(args: Array<String>) {
        val list= listOf(1,2,3,4,5,6,7,8,9)
        val result=list.filter {
            it%2==0
        }
        println(result)
    }
    

    运行结果如下:


    Snipaste_2020-02-17_10-31-55.png
    4.3 kotlin 中的 特殊函数
    4.3.1 run()函数

    该函数实际上可以说是let和with两个函数的结合体,run函数接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式。

    示例代码如下:

    fun main(args: Array<String>) {
        FunKotlin().myFun()
        run {
            FunKotlin().myFun()
        }
        run {
            println("kotlin")
        }
    }
    
    class FunKotlin{
    
        fun myFun():String{
            println("开始执行myFun()函数")
            return "kotlin 中的特殊函数"
        }
    }
    

    运行结果如下:

    Snipaste_2020-02-17_10-31-55.png

    在上面的代码中,我们定义了myFun()函数并通过run() 进行调用,调用的结果即为myFun()的结果。

    run() 源码如下:

    /**
     * Calls the specified function [block] and returns its result.
     */
    @kotlin.internal.InlineOnly
    public inline fun <R> run(block: () -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block()
    }
    

    如代码所示:我们传入block()参数,最终返回了block() 的执行结果。

    4.3.2 apply()函数

    源码如下:

    /**
     * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
     */
    @kotlin.internal.InlineOnly
    public inline fun <T> T.apply(block: T.() -> Unit): T {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        block()
        return this
    }
    

    如代码所示:我们传入 block() 函数,先是调用了block()函数,然后返回当前的调用者对象this,也就是说先执行完block()代码块逻辑后,再次返回当前的调用者对象。

    示例代码如下:

    fun main(args: Array<String>) {
        testApply()
    }
    
    fun testApply(){
        val list= mutableListOf<String>()
        list.add("A")
        list.add("B")
        list.add("C")
        list.add("D")
        list.add("E")
        println("普通写法:list=${list}")
    
        val applyList=mutableListOf<String>().apply {
            add("A")
            add("B")
            add("C")
            add("D")
            add("E")
            println("使用apply 函数写法 this=${this}")
        }
    }
    

    运行结果如下:


    Snipaste_2020-02-17_10-31-55.png

    如代码所示:我们的需求是创建一个集合并向其中添加元素"A",“B”,"C",“D”,“E”,然后打印出该集合,相比普通写法,使用apply() 函数显然简洁了许多。

    4.3.3 let() 函数

    let扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作

    源码如下:

    /**
     * Calls the specified function [block] with `this` value as its argument and returns its result.
     */
    @kotlin.internal.InlineOnly
    public inline fun <T, R> T.let(block: (T) -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block(this)
    }
    

    如代码所示,我们重点看最后一行return block(this),就是说把当前调用对象作为参数传入block()代码块中。

    示例代码如下:我们以Android中在适配器 adapter 中进行网络图片的加载。

    context?.let {         
      Glide.with(it).load(item.envelopePic).crossFade().into(helper.getView<ImageView>(R.id.iv_envelopePic))
                    }
    
    

    在上面的代码中,it 指代的即是 context,意为上下文。</pre>

    4.3.4 also()函数

    源码如下:

    /**
     * Calls the specified function [block] with `this` value as its argument and returns `this` value.
     */
    @kotlin.internal.InlineOnly
    @SinceKotlin("1.1")
    public inline fun <T> T.also(block: (T) -> Unit): T {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        block(this)
        return this
    }
    

    看最后两行代码,先是执行了block(this),随后返回this,即当前的调用者对象。

    示例代码如下:

    fun testAlsoFun() {
        val a = "ABC".also {
            println(it) //输出:ABC
        }
        println(a) //输出:ABC
        a.let {
            println(it) //输出:ABC
        }
    }
    fun main(args: Array<String>) {
        testAlsoFun()
    }
    

    在上面的代码中,字符串“ABC”调用了also(),会打印出调用者 “ABC”.

    4.3.5 with() 函数

    源码如下:

    /**
     * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
     */
    @kotlin.internal.InlineOnly
    public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return receiver.block()
    }
    
    

    我们看到with()函数传入了一个接收者对象receiver,然后使用该对 象receiver去调用传入的Lambda代码块receiver.block()。

    • 示例代码在 Android中我们初始化一个控件并给其赋值,Java 语言实现如下:
    TextView text=(TextView)findViewById(R.id.tv_text)
    text.setText("哈哈哈")
    text.setTextSize(23)
    
    • kotlin 语言实现如下:
    with(tv_text){
    text="哈哈哈"
    textSize=23
    }
    

    在上面的代码中,实现的功能是一样的,但是显然 kotlin 语言更加的简洁。

    5.扩展函数与属性

    相关文章

      网友评论

          本文标题:kotlin 学习教程(五) |函数和函数式编程

          本文链接:https://www.haomeiwen.com/subject/huhbfhtx.html