高阶函数

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

所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(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. Win10安装curl

    参看博客:https://blog.csdn.net/qq_37289115/article/details/106665123

  2. 如何基于 Agora Android SDK 在应用中实现视频通话?

    在很多产品,实时视频通话已经不是新鲜的功能了,例如视频会议.社交应用.在线教育,甚至也可能出现在一些元宇宙的场景中. 本文将教你如何通过声网Agora 视频 SDK 在 Android 端实现一个视频 ...

  3. 当我把ChatGPT拉进群聊里,我的朋友都玩疯了

    前言 近期ChatGPT可以说是太火了,问答.写论文.写诗.写代码,只要输入精确的prompt,他的表现总是让人惊喜.本着打不过就加入的原则.要是把ChatGPT拉入群聊中,会是怎样一番场景?说做就做 ...

  4. UI/UE设计学习路线图(超详细)

    很多小伙伴认为ui设计很简单,就是用相关的软件设计制作图片.界面等.其实不然,UI设计融合了很多学科内容.要从一个完全没有基础的人成长为一个ui设计者,该如何学习呢?主要分为基础阶段和专业课程阶段,其 ...

  5. Java 计算两个日期的时间间隔

    @Test public void durationTest(){ Temporal begin = LocalDateTime.of(2000,1,1,0,0); Temporal end = Lo ...

  6. C++ (伪)随机数生成

    #include <iostream> #include <random> namespace random { // 从系统获取随机数作为种子 std::random_dev ...

  7. 为什么说 ICMP 协议是网络最强辅助

    大家好,我是风筝 轻解网络系列又来了.已有高清 PDF 版本可以离线阅读了,全册 65 页,如果有需要离线版的高清 PDF 可以直接下载. 今天咱们说说 ICMP 协议.ICMP 可谓是网络世界中的最 ...

  8. 使用vSphere Update Manager 升级 ESXi 主机

    使用vSphere Update Manager 升级 ESXi 主机 vSphere Update Manager  vSphere Update Manager 是用于升级.迁移.更新和修补群集主 ...

  9. 【CTF】关于 .init .fini .init_array .fini_array 日志 2019.7.16 pwn

    查找资料的高效性 retn 返回到栈顶地址 关于 .init .fini .init_array .fini_array 其中存放着的是在main函数执行前执行的代码,由__libc_start_ma ...

  10. 无法加载 DLL“xxxx.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。

    有一台服务器在执行接口的时候遇到了这样一个问题: 其他服务器上都没有这个问题,IIS部署好的项目目录的bin文件夹下是有这个dll的,但却提示无法加载,在网上找了好多帖子,终于发现了问题. 首先用De ...