高阶函数

高阶函数是将函数用作参数或返回值的函数,还可以把函数赋值给一个变量。

所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(A, B) -> C 表示接受类型分别为 A 与 B 两个参数并返回一个 C 类型值的函数类型。 参数类型列表可以为空,如 () -> A,Unit 返回类型不可省略。

(Int) -> String

函数类型表示法可以选择性地包含函数的参数名:(x: Int, y: Int) -> Point。 这些名称可用于表明参数的含义。

(Button, ClickEvent) -> Unit

如需将函数类型指定为可空,请使用圆括号:((Int, Int) -> Int)?

    fun a(funParam: (Int) -> String): String {
return funParam(1)
} fun b(param: Int): String {
return param.toString()
}

调用

a(::b)
var d = ::b
b(1) // 调用函数
d(1) // 实际上会调用 d.invoke(1)
(::b)(1) // 用对象 :: b 后面加上括号来实现 b() 的等价操作, 实际上会调用 (::b).invoke(1)
b.invoke(1) // 报错

对象是不能加个括号来调用的,但是函数类型的对象可以。为什么?因为这其实是个假的调用,它是 Kotlin 的语法糖,实际上你对一个函数类型的对象加括号、加参数,它真正调用的是这个对象的 invoke() 函数

双冒号

:: 创建一个函数引用或者一个类引用

函数引用

fun isOdd(x: Int) = x % 2 != 0

我们可以很容易地直接调用它(isOdd(5)),但是我们也可以将其作为一个函数类型的值,例如将其传给另一个函数。为此,我们使用 :: 操作符:

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))

这里 ::isOdd 是函数类型 (Int) -> Boolean 的一个值。

如果我们需要使用类的成员函数或扩展函数,它需要是限定的,例如 String::toCharArray。

    val args: Array<String> = arrayOf("1", "2")
args.filter(String::isNotEmpty) class PdfPrinter {
fun println(any: Any) {
kotlin.io.println(any) //重名了可以用包名调用
}
}
val pdfPrinter = PdfPrinter()
args.forEach(pdfPrinter::println)

类引用

val c = MyClass::class

该引用是 KClass 类型的值

请注意,Kotlin 类引用与 Java 类引用不同。要获得 Java 类引用, 请在 KClass 实例上使用 .java 属性。

平时写的类,其信息都可以在这个KClass来获取

属性引用

data class MediaItem(val title: String, val url: String)

var items= mutableListOf<MediaItem>()
items
.sortedBy { it.title }
.map { it.url }
.forEach { print(it) } items
.sortedBy(MediaItem::title)
.map(MediaItem::url)
.forEach(::println)

匿名函数

没有名字的函数

要传一个函数类型的参数,或者把一个函数类型的对象赋值给变量,除了用双冒号来拿现成的函数使用,你还可以直接把这个函数挪过来写:

fun b(param: Int): String {
return param.toString()
} a(fun b(param: Int): String {
return param.toString()
}); val d = fun b(param: Int): String {
return param.toString()
} //名字没意义,省略
a(fun(param: Int): String {
return param.toString()
});
val d = fun(param: Int): String {
return param.toString()
}

如果你在 Java 里设计一个回调的时候是这么设计的:

public interface OnClickListener {
void onClick(View v);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}

使用的时候是这么用的:

view.setOnClickListener(new OnClickListener() {
@Override
void onClick(View v) {
switchToNextPage();
}
});

kotlin写法

fun setOnClickListener(onClick: (View) -> Unit) {
this.onClick = onClick
}
view.setOnClickListener(fun(v: View): Unit) {
switchToNextPage()
})

Lambda写法:

view.setOnClickListener({ v: View ->
switchToNextPage()
})

Lambda 表达式

简化匿名函数,代码更简洁

    view.setOnClickListener({ v: View ->
switchToNextPage()
}) //如果 Lambda 是函数的最后一个参数,你可以把 Lambda 写在括号的外面:
view.setOnClickListener() { v: View ->
switchToNextPage()
}
//而如果 Lambda 是函数唯一的参数,你还可以直接把括号去了:
view.setOnClickListener { v: View ->
switchToNextPage()
}
//另外,如果这个 Lambda 是单参数的,它的这个参数也省略掉不写:
//根据上下文推导,根据最后一行代码来推断出返回值类型
view.setOnClickListener {
switchToNextPage()
}

Lambda 表达式的完整语法形式如下:

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y }

多参数例子:

fold函数:将所提供的操作应用于集合元素并返回累积的结果

val items = listOf(1, 2, 3, 4, 5)

// Lambdas 表达式是花括号括起来的代码块。
items.fold(0, {
// 如果一个 lambda 表达式有参数,前面是参数,后跟“->”
acc: Int, i: Int ->
print("acc = $acc, i = $i, ")
val result = acc + i
println("result = $result")
// lambda 表达式中的最后一个表达式是返回值:
result
}) // lambda 表达式的参数类型是可选的,如果能够推断出来的话:
val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i })

输出:

acc = 0, i = 1, result = 1
acc = 1, i = 2, result = 3
acc = 3, i = 3, result = 6
acc = 6, i = 4, result = 10
acc = 10, i = 5, result = 15
joinedToString = Elements: 1 2 3 4 5

总结:

函数不能直接传递或者赋给某个变量,需要函数类型实例化,有三种方式:

使用已有声明的可调用引用

1.函数引用

使用函数字面值的代码块

2.匿名函数

3.lambda 表达式

Kotlin 的高阶函数、匿名函数和 Lambda 表达式

例子

实现接口

var onVideoStartCallBack: (() -> Unit)? = null

onVideoStartCallBack?.invoke()

videioView.onVideoStartCallBack = {

}

函数里实现接口

object UploaderListHelper {

    fun startTaskUpload(activity: Activity, startCallBack: ((Int) -> Unit)?) {
startCallBack.invoke(position)
}
} UploaderListHelper.startTaskUpload(activity) {
refreshProgress(it)
}

作用域函数

Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:let、run、with、apply 以及 also。

这些函数基本上做了同样的事情:在一个对象上执行一个代码块。不同的是这个对象在块中如何使用,以及整个表达式的结果是什么。

目的:简洁

 val person = findPerson();
//person是可null的,所以需要?
println(person?.age)
println(person?.name)
//上面太麻烦,findPerson加了?,所以后面不需要了,减少的判空操作。let可以安全调用
findPerson()?.let { person ->
person.work()
println(person.age)
}
//还可以更简洁,person也不用写
findPerson()?.apply {
work()
println(age)
}

使⽤时可以通过简单的规则作出一些判断

返回自身

返回值是它本身

从 apply 和 also 中选

作⽤域中使⽤ this 作为参数选择 apply

val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)

作⽤域中使⽤ it 作为参数选择 also

val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")

with 非扩展函数

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}

不需要返回自身

从 run 和 let 中选择

作用域中使用 this 作为参数,选择 run

作用域中使用 it 作为参数,选择 let, 适合配合空判断的时候

val service = MultiportService("https://example.kotlinlang.org", 80)

val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
} // 同样的代码如果用 let() 函数来写:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}

it作为参数的好处

let 允许我们自定义参数名字,使可读性更强,如果倾向可读性可以选择 T.let

Kotlin难点的更多相关文章

  1. Kotlin 喧嚣过后,谈谈 Java 程序员未来的出路

    http://blog.jobbole.com/111422 Java 生态圈 Java 的生态环境开放.自由,在Sun/Oracle.Google.Apache.Eclipse基金会等各大厂商,还有 ...

  2. Android-贪吃蛇小游戏-分析与实现-Kotlin语言描述

    Android-贪吃蛇小游戏-分析与实现-Kotlin语言描述 Overview 本章的主要的内容是贪吃蛇小游戏的分析和实现,关于实现的具体代码可以在,文章底部的github的链接中找到. 整个游戏通 ...

  3. Kotlin 学习使用之旅(二)

    为什么从二开始呢?再此之前已经有了一篇了,那是刚知道kotlin的时候草(chao)来(chao)的并且学习一篇, 这次是自己在项目中正式使用并且遇到的一些问题记录,供kotlin新入门的童鞋参考,避 ...

  4. Kotlin实战案例:带你实现RecyclerView分页查询功能(仿照主流电商APP,可切换列表和网格效果)

    随着Kotlin的推广,一些国内公司的安卓项目开发,已经从Java完全切成Kotlin了.虽然Kotlin在各类编程语言中的排名比较靠后(据TIOBE发布了 19 年 8 月份的编程语言排行榜,Kot ...

  5. 这是一份非常适合收藏的Android进阶/面试重难点整理

    写在前面 记得我大二时“不务正业”地自学Android并跟了老师做项目,到大三开始在目前的公司实习,至今毕业已有几年多,学习Android已经6.7年多了!但总感觉知识点很零散,并且不够深入,遇到瓶颈 ...

  6. Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

    作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...

  7. 用Kotlin实现Android定制视图(KAD 06)

    作者:Antonio Leiva 时间:Dec 27, 2016 原文链接:https://antonioleiva.com/custom-views-android-kotlin/ 在我们阅读有关c ...

  8. Kotlin与Android SDK 集成(KAD 05)

    作者:Antonio Leiva 时间:Dec 19, 2016 原文链接:https://antonioleiva.com/kotlin-integrations-android-sdk/ 使用Ko ...

  9. Kotlin的android扩展:对findViewById说再见(KAD 04)

    作者:Antonio Leiva 时间:Dec 12, 2016 原文链接:http://antonioleiva.com/kotlin-android-extensions/ 你也许已厌倦日复一日使 ...

  10. Kotlin类:功能更强、而更简洁(KAD 03)

    作者:Antonio Leiva 时间:Dec 7, 2016 原文链接:http://antonioleiva.com/classes-kotlin/ Kotlin类尽可能简单,这样用较少的代码完成 ...

随机推荐

  1. Web自动化——介绍与安装以及第一个web自动化程序(一)

    1. 为什么要做Web自动化测试 什么是web自动化测试 让程序代替人,去验证网页上功能的过程 web自动化测试与手工测试的比较 web自动化测试执行的测试用例是手工功能测试的子集 web自动化测试的 ...

  2. mybatis-spring注解MapperScan的原理

    很多开发者应该都知道,我们只使用@MapperScan这个注解就可以把我们写的Mybatis的Mapper接口加载到Spring的容器中,不需要对每个Mapper接口加@Mapper这个注解了,加快了 ...

  3. NOIP2022游记

    NOIP2022游记 今年是第二次考NOIP了,去年第一次考的时候没学过什么东西,混了个省二.今年以高中生的身份考,不仅仅是要省一,还得拿个不错的名次,任务不小. 考试当天早上校园里的雾很大,不知道会 ...

  4. uniapp踩坑必备笔记

    1.[配置]应用版本号名称有一个规则的字符串:1.0.0,规则是:大版本号,中版本号,小版本号. 2.[配置]应用版本号中的小版本号不能超过9,超过9的需要向上一个版本号进一(逢十进一). 3.[配置 ...

  5. Tomcat启动报错,Server Tomcat v8.0 Server at localhost failed to start

    Eclipse 中Tomcat 启动报错Eclipse的提示窗口  Server Tomcat v8.0 Server at localhost failed to start .日志输出中报   F ...

  6. python标准模块之subprocess

    subprocess --- 子进程管理 源代码: Lib/subprocess.py 写在前面: 感觉也就这俩有用: subprocess.run() subprocess.Popen()   w下 ...

  7. pysimplegui之运行多个窗口

    运行多个窗口 这就是 PySimpleGUI 继续简单的地方,但问题空间刚刚进入"复杂"领域. 如果您希望在事件循环中运行多个窗口,那么有两种方法可以做到这一点. 当第二个窗口可见 ...

  8. [Linux]常用命令之【history】#查看历史操作#

    1 历史记录: history history命令就是历史记录. 它显示了在终端中所执行过的所有命令的历史. history //显示终端执行过的命令 history 10 //显示最近10条终端执行 ...

  9. [IDE]IDEA build artifacts过程很慢的解决方案[转载]

    解决方案 可能1 可能是缓存的文件太多了导致: File->Invalidate Caches /Restart,清理缓存, 并重启IDEA.重启之后,会重建索引, 此过程较慢, 但build的 ...

  10. [Java]排序算法>选择排序>【简单选择排序】(O(n*n)/不稳定/)

    1 选择排序 1.1 算法思想 每一趟从待排序的记录中选出关键字最小的记录,按顺序放在已排序的记录序列的最后(or最前面),直到全部排完位置. 1.2 算法特征 属于[选择排序] 简单选择排序 堆排序 ...