五,Kotlin-函数进阶

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容