美文网首页
五,Kotlin-函数进阶

五,Kotlin-函数进阶

作者: 从心开始的我 | 来源:发表于2019-12-11 18:05 被阅读0次

    1,高阶函数

    定义
    • 函数的参数类型包含函数或者返回值为函数类型
    • 如果函数的参数只有一个参数或者最后一个参数为一个lambda表达式则可以把该函数提取到"()"外部,如果该函数没有其他的参数可以把"()"省略

    如下例中三个形式不同,意义相同

    IntArray(5) {
            it
        }.forEach({ele: Int ->
            ele + 1
        })
        
        IntArray(5) {
            it
        }.forEach(){ele: Int ->
            ele + 1
        }
        
        IntArray(5) {
            it
        }.forEach{ ele: Int ->
            ele + 1
        }
    
    这里有关lambda表达式有几点需要注意

    lambda表达式的类型推导有个顺序,实际上lambda最后一行不管表达式的值的类型是什么,我们都可以把他的返回类型声明为Unit,当然你不声明的话就像你说的是返回表达式的类型也就是Int了。这里foreach的参数类型是确定的,等于为参数的lambda表达式声明了类型必须为int->unit。

    val func = { ele: Int ->
            ele + 1
        }
    这个func 相当于已经声明了是 Int-> Int  所以不能再传入foreach中,
    
         { ele: Int ->
            ele + 1
        }
    没有变量接收,所以可以被foreach声明为int ->Unit
    

    接下来写个例子

    1,写个求函数运行时长的函数

    fun cast(func: () -> Unit) {
        val start = System.currentTimeMillis();
        func()
        println(System.currentTimeMillis() - start)
    }
    

    2,写一个求斐波那契数的函数

    fun fibonacci(): () -> Long {
        var first = 0L
        var second = 1L
        return {
            val next = first + second
            val current = first
            first = second
            second = next
            current
        }
    }
    

    3,求出得到前10个斐波那契数所需时间

    cast {
            val funcc= fibonacci()
            for (i in 1..10){
                println(funcc())
            }
        }
    

    2,内联函数

    定义

    • 内联函数:就是把函数的参数直接挪到函数体内进行执行
    • 一个普通的函数 加上"inline" 即可定义为内联函数
    • 高阶函数更适合做内联函数,因为本身高阶函数会再次调用或返回函数,
    • 好处,减少了函数的调用,节省性能的开销
    fun main() {
        castTime {
            println("HelloWord")
        }
    }
    
    inline fun castTime(func: () -> Unit) {
        val startTime = System.currentTimeMillis()
        func()
        // 编译器在编译时相当于把函数体挪到这个位置
        //println("HelloWord")
        println(System.currentTimeMillis() - startTime)
    }
    
    高阶函数的内联
    • 函数本身被内联到调用处
    • 函数的函数参数被内联到调用处
    内联高阶函数的return

    示例

    val arrays = arrayOf(1, 2, 3, 4)
        arrays.forEach {
            if (it == 3) return@forEach
            println("---------$it---------")
        }
    

    结果

    ---------1---------
    ---------2---------
    ---------4---------
    
    内联函数无法像Java中那样通过break结束本次遍历,只能通过标签,跳出本次内联函数的调用

    在lambda表达式中直接return的写法也叫local return,与之相对应的 如果在lambda表达式外部调用的 叫 non-local return ;
    其实简单理解就是带内联函数标签的中断叫localreturn ;不带内联函数标签的中断叫non-local return

    non-local return 返回的是主调用函数,直接中断内联函数的调用

    例子:该次return 直接返回到main函数,之后的也不再执行

    fun main() {
        val arrays = arrayOf(1, 2, 3, 4)
        arrays.forEach {
            if (it == 3) return
            println("---------$it---------")
        }
     println("------------------")
    }
    
    并不是所以有得内联函数都可以进行non-local return
    crossline关键字

    例如:由于block定义和调用处于不同的上下文,此时编译器不会通过,除非加上crossline 关键字表示 block 肯定不会出现non-local return,这样才行

    inline  fun runnable(crossinline block:()->Unit):Runnable{
        return object :Runnable{
            override fun run(){
                block()
            }
        }
    }
    
    内联属性
    内联函数的限制

    3,几个有用的函数

    具体用法:

    class Person(var name: String, var age: Int, var sex: Boolean) {
    }
    
    fun main() {
        val person = Person("WJF", 25, true)
        println("${person.name}-${person.age}-${person.sex}")
        person.let {
            it.name = "11111"
            it.age = 1
            it.sex = false
        }
        person.run {
            name = "22222"
            age = 2
            sex = false
        }
        println("${person.name}-${person.age}-${person.sex}")
    
        var person2 = person.also {
            it.name = "33333"
            it.age = 3
            it.sex = true
        }
        println("${person.name}-${person.age}-${person.sex}")
        var person3 = person.apply {
            name = "44444"
            age = 4
            sex = false
        }
        println("${person.name}-${person.age}-${person.sex}")
    
        println("${person2 == person3}")
        println("${person2 === person3}")
    }
    

    运行结果

    WJF-25-true
    22222-2-false
    33333-3-true
    44444-4-false
    true
    true
    

    结果我们得出结论

    • 可以看出其实let()和run()只有内部引用不同,同样都没有返回值
    • also()和apply()只有内部引用不同,同时返回了Person对象,但是这个对象都是原始的person 只是属性变了引用没变
    user 的使用

    将读出build.gradle文件的每一行内容并打印出来

    File("build.gradle").inputStream().reader().buffered()
            .use {
                val readLines = it.readLines()
                readLines.forEach { ele: String ->
                    println(ele)
                }
            }
    

    4,集合变换与序列

    <1>集合的变换
    • filter :保留满足条件的元素
    • map :集合中的所有元素一 一映射到其他元素结构新集合
    • floatMap : 集合中所有的元素一 一映射到新集合并合并这些集合得到新的集合

    个人理解

    1,filter就是过滤元素形成新的集合
    2,map就是针对当前集合对其元素进行变换,之后组成新的集合
    3,floatMap 就是对集合中的每个元素进行操作形成n个新的集合 并且将他们组合在一起

    举例

    fun main() {
        val arr = arrayOf(1, 2, 3, 4, 5)
    
        arr.asSequence()
            .filter {
                println("filter${it}")
                it % 2 == 0
            }.forEach {
                println("$it")
            }
        //filter就是过滤元素
        arr.filter {
            println("filter${it}")
            it % 2 == 0
        }.forEach {
            println("$it")
        }
        //map就是针对当前集合对其元素进行变换,形成新的集合
        arr.map {
            "$it"
        }.forEach {
            println(it)
        }
        //floatMap 就是对集合中的每个元素进行操作形成n个新的集合  并且将他们组合在一起
        arr.flatMap {
            (1..it)
        }.forEach {
            println(it)
        }
    //    arr.asSequence()
    //        .flatMap {
    //            (1..it).asSequence()
    //        }.forEach {
    //            println(it)
    //        }
    
    }
    
    asSequence() 是数据流的意思,相当于数据流的"懒加载",像上个例子中,如果加上asSequence()在没有出口(例如forEach)的时候是不会执行的,而且执行顺序 变成集合中数据一个一个的网下执行,不是统一变换后在执行下一步

    例如

    val arr = arrayOf(1, 2, 3, 4, 5)
        arr.filter {
            println("filter${it}")
            it % 2 == 0
        }.forEach {
            println("$it")
        }
    

    执行结果为

    filter1
    filter2
    filter3
    filter4
    filter5
    2
    4
    

    在加上.asSequence()之后

     val arr = arrayOf(1, 2, 3, 4, 5)
        arr.asSequence()
            .filter {
                println("filter${it}")
                it % 2 == 0
            }.forEach {
                println("$it")
            }
    

    执行结果为

    filter1
    filter2
    2
    filter3
    filter4
    4
    filter5
    
    <2>集合的聚合操作

    聚合操作其实就需要将集合里的元素进行运算

    • sum : 集合所有元素求和
    • reduce : 将元素依次按规则聚合,结果与元素类型一致
    • fold : 给定初始化值,将元素按规则聚合,结果与初始化值类型相同

    例子

    val arr = arrayOf(1, 2, 3, 4)
        val arrStr = arrayOf("11", "21", "31", "14")
        //sum操作符
        println(arr.sum())
        //reduce操作符
        val count = arr.reduce { acc, i ->
            acc + i
        }
        println(count)
        //fold操作
        arr.fold(StringBuilder()) { acc, i ->
            acc.append(i)
        }.forEach {
            println(it)
        }
        //fold操作
        arr.fold("") { acc, i ->
            "$acc$i"
        }.forEach {
            println(it)
        }
        //zip变换
        arr.zip(arrStr).forEach {
            println("${it.first}--${it.second}")
        }
    

    5,SAM转换

    Java的lambda表达式没有自己的类型,必须有一个接口来接收它,而且这个接口必须是单一方法,实际上Java中的lambda的类型就是"单一方法的接口类型" ,也可以是Kotlin的接口进行接收

    Kotlin中的匿名内部类

    写法

    object:接口名称{
         方法(){
              xxxxxx
          }
    }
    

    对于Kotlin调用Java的匿名内部类时,如果写成lambda形式,Kotlin自动会把lambda转换成匿名内部类的形式

    坑!!!!!!!!!!!

    对于Kotlin调用Java代码时有些需要添加和移除listener的,不能再用lambda表达式了,因为移除的不是同一个对象

    正确操作

    定义一个Java类

    public class EventManager {
    
        private HashSet<OnEventListener> hashSet=new HashSet<>();
        public interface OnEventListener {
            public void onEvent(int i);
        }
        public void addEventListener(OnEventListener eventListener){
            hashSet.add(eventListener);
        }
        public void removeEventListener(OnEventListener eventListener){
            hashSet.remove(eventListener);
        }
    }
    

    正确调用

    val manager=EventManager()
        //使用匿名内部类的形式先定义出具体对象
        val onEvent=EventManager.OnEventListener {
            println(it)
        }
        //添加该对象
        manager.addEventListener(onEvent)
        //移除该对象
        manager.removeEventListener(onEvent)
    
    

    小案例:读取项目的build.gradle文件的每个字符出现的次数

    //读取build.gradle文件各个字符出现的次数
        File("build.gradle")
            .readText()//文件到String
            .toCharArray()//变成字符数组
            .filter { !it.isWhitespace() }//过滤空白字符
            .groupBy { it }//以每个字符为分组依据进行分组
            .map {
                it.key to it.value.size//拿到每个字符对应的size
            }.let {
                println(it)//输出
            }
    

    6,DSL

    相关文章

      网友评论

          本文标题:五,Kotlin-函数进阶

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