高阶函数

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

所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(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. Masa Framework源码解读-02缓存模块(分布式缓存进阶之多级缓存)

    序言 ​ 今天这篇文章来看看Masa Framework的缓存设计,上一篇文章中说到的MasaFactory的应用也会在这章节出现.文章中如有错误之处还请指点,咱们话不多说,直入主题. Masa Fr ...

  2. Linux & 标准C语言学习 <DAY11>

    一.指针     1.什么是指针         指针是一种特殊的数据类型,使用指针可以定义指针变量,指针变量存储的是整形数据,该数据代表了内存的编号(地址),可以通过这个编号访问到对应的内存     ...

  3. 【读书笔记】排列研究-模式避免-续篇Pattern Avoidance

    目录 多项式递归Polynomial Recursions P-recursive和c-recursive定义 例子:卡特兰数序列是P-recursive(或者说D-finite) 两个说明\(S_n ...

  4. aspnetcore微服务中使用发件箱模式实例

    aspnetcore微服务种服务之间的通信一般都有用到消息中间件,如何确保该服务的持久层保存创建的数据同时又把消息成功投递到了关联服务,关联服务做对应的处理. 下面就以一个简单的例子来演示实现方式之一 ...

  5. 关于MySQL建立库表时大写自动转换为小写的解决方案

    mysql 5.6以上windows对大小写敏感要在my.ini中的[mysqld]下面设置lower_case_table_names = 2 网上有的要改成0 亲测报错 [○・`Д´・ ○]

  6. Shell---控制流程

    操作系统: RHEL7.x 或CentOS 7.x 最小化安装 配置好固定的IP,能访问互联网 配置好yum源(yum repolist 可以查看yum源) 本地光盘 挂载光盘,开机自动挂载 vim ...

  7. What's the best way to read and understand someone else's code?

    Find one thing you know the code does, and trace those actions backward, starting at the end Say, fo ...

  8. [Java/LeetCode]算法练习:二进制间距(868/simple)

    1 题目描述 题目来源: https://leetcode-cn.com/problems/binary-gap/ 给定一个正整数 n,找到并返回 n 的二进制表示中两个 相邻 1 之间的 最长距离 ...

  9. Go语言微服务框架go-micro(入门)

    Micro用于构建和管理分布式系统,是一个工具集,其中go-micro框架是对分布式系统的高度抽象,提供分布式系统开发的核心库,可插拔的架构,按需使用 简单示例 编写protobuf文件: synta ...

  10. Docker MariaDB配置主从复制

    编写主节点配置文件master.cnf: [client] # 默认字符集 default-character-set=utf8mb4 [mysqld] # 字符集 character-set-ser ...