Kotlin基础——高阶函数和内联函数

高阶函数

高阶函数以另一个函数作为参数或者返回值,其可用Lambda或函数引用表示

函数类型

下面将Lambda存储在sum变量中,其是函数类型

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

完整的函数类型为(para1,prar2…) -> returnValue

val a: Int = 0
val sum: (Int, Int) -> Int = { x, y -> x + y }
val sum2: ((Int, Int) -> Int) = { x, y -> x + y }	//感觉这样可读性更高

若返回类型可空则为

val canReturnNull: (Int) -> Int? = { null }

若变量类型本身为空,而不是函数类型返回值可空,则为

val funOrNull: ((Int) -> Int?)? = null

调用作为参数的函数

如下,参数为函数类型,并在内部调用,根据传进来的参数实现不同的操作

fun twoAndThree(operation: (Int, Int) -> Int) {val result = operation(2, 3)println("result = $result")
}twoAndThree { a, b -> a + b }
twoAndThree { a, b -> a * b }

如下对String实现filter,遍历字符串,若符合条件则添加到StringBuilder

fun String.filter(predicate: (Char) -> Boolean): String {val sb = StringBuilder()for (index in 0 until length) {val element = get(index)if (predicate(element))sb.append(element)}return sb.toString()
}
println("1abc".filter { it in 'a'..'z' })

Java中使用函数类型

一个函数类型的变量是FunctionN接口的一个实现,其内部的invoke方法调用Lambda函数体

fun process(f: (Int) -> Int){println(f(1))
}

上面Kotlin函数接收一个函数类型,并调用该函数传入1,打印返回值,在Java中可直接传递Lambda

public class Test {public static void run() {JoinKt.process(number -> number + 1);}
}

而在Java8之前可显示创建Function1,通过invoke代替Lambda

public class Test {public static void run() {JoinKt.process(new Function1<Integer, Integer>() {@Overridepublic Integer invoke(Integer integer) {integer = integer + 1;System.out.println(integer);return integer;}});}
}

若使用带Lamba的扩展函数,需要将调用者作为第一个参数传递,且不能用void代替Unit作为返回值

public class Test {public static void run() {List<String> strings = new ArrayList<>();strings.add("1");CollectionsKt.forEach(strings, s -> {System.out.println(s);return Unit.INSTANCE;});}
}

函数类型的参数设置默认值

fun <T> joinToString(collection: Collection<T>,separator: String = "",prefix: String = "",postfix: String = ""
): String {val result = StringBuilder(prefix)for ((index, element) in collection.withIndex()) {if (index > 0)result.append(separator)result.append(element)}result.append(postfix)return result.toString()
}

对于上面代码,添加一个函数类型的参数,并指定默认行为为拼接字符串

fun <T> joinToString(collection: Collection<T>,separator: String = "",prefix: String = "",postfix: String = "",transform: (T) -> String = { it.toString() }
): String {val result = StringBuilder(prefix)for ((index, element) in collection.withIndex()) {if (index > 0)result.append(separator)result.append(transform(element))}result.append(postfix)return result.toString()
}

在实际调用时,可传入Lambda修改默认行为

val letters = listOf("a", "b")
println(joinToString(letters))
println(joinToString(letters, transform = { it.toUpperCase() }))

函数类型的参数设置null值

将函数类型的参数设置为可空,并在调用时检查

fun foo(callback: (() -> Unit)?) {if (callback != null) {callback()}
}

或者显式非空调用invoke

fun foo(callback: (() -> Unit)?) {callback?.invoke()
}

返回函数的函数

如下函数根据运输方式返回不同的计算方式,getCost()根据不同的Delivery返回一个参数为Order,返回值为Double的函数

enum class Delivery { STANDARD, EXPEDITED }class Order(val itemCount: Int)fun getCost(delivery: Delivery): (Order) -> Double {if (delivery == Delivery.EXPEDITED) {return { order -> 2.0 * order.itemCount }}return { order -> 1.0 * order.itemCount }
}

在调用时,使用val变量接收该函数

val cost = getCost(Delivery.EXPEDITED)
println("cost = " + cost(Order(3)))

内联函数

使用 inline 修饰的函数被使用时编译器不会生成函数调用的代码,而是使用真实代码替换每一次的函数调用

inline fun <T> synchronized(lock: Lock, action: () -> T): T {lock.lock()try {return action()} finally {lock.unlock()}
}
fun foo(l: Lock) {println("Before lock")synchronized(l) {println("Action")}println("After lock")
}

对于内联函数synchronized的调用,会被转化为

fun foo(l: Lock) {println("Before lock")l.lock()try {println("Action")} finally {l.unlock()}println("After lock")
}

内联函数的限制

函数类型的变量作为内联函数的参数,不能被内联,因为只有当外层的内联展开后,其中的Lambda才会被正常调用

inline fun <T> synchronized(lock: Lock, action: () -> T): T {lock.lock()try {return action()} finally {lock.unlock()}
}class LockOwner(val lock: Lock) {fun runUnderLock(body: () -> Unit) {synchronized(lock, body)}
}

runUnderLock()将函数类型的变量作为参数body,传递给synchronized(),只能内联synchronized(),而不能一并内联runUnderLock()

class LockOwner(val lock: Lock) {fun runUnderLock(body: () -> Unit) {lock.lock()try {body()} finally {lock.unlock()}}
}

如果参数为Lambda且在某个地方被保存,不能被内联,如Sequence中操作集合的方法

class Man(val name: String, val sex: String)fun Man.toWoman(transform: (String) -> String): WoMan {return WoMan(this, transform)
}
class WoMan(val man: Man, val change: (String) -> String) {override fun toString(): String {return "name=${man.name},sex=" + change(man.sex)}
}

如上,Lambda传递给Woman的构造函数并保存到change属性,toWoman()不能声明为内联函数

在这里插入图片描述
用noinline修饰的参数,不能被内联

inline fun foo(inlined: () -> Unit, noinline: () -> Unit) {}

使用use关闭流

在Java中操作文件通常使用try-with-resource

static String readFirstLineFromFile(String path) throws IOException {try (BufferedReader br = new BufferedReader(new FileReader(path))) {return br.readLine();}
}

而在Kotlin中可以使用uer代替,其会自动关闭流

fun readFirstLineFromFile(path: String): String {BufferedReader(FileReader(path)).use { br ->return br.readLine()}
}

高阶函数中的控制流

使用标签返回

内联Lambda中的return语句默认返回到外层函数,如下不会打印 not Fount

class Person(val name: String, val age: Int)fun find() {val list = listOf(Person("A", 18), Person("A", 18))list.forEach {if (it.name == "A") {println("Found")return}}println("not Found")
}

若使用标签,可实现Lambda的局部返回,如下使用@label表示标签,会打印 not Found

class Person(val name: String, val age: Int)fun find() {val list = listOf(Person("A", 18), Person("A", 18))list.forEach label@{if (it.name == "A") {println("Found")return@label}}println("not Found")
}

使用Lambda作为参数的函数名可以作为标签,如下使用foreach作为标签,但如果显示指定了Lambda的标签,再使用函数名作为标签会失效

class Person(val name: String, val age: Int)fun find() {val list = listOf(Person("A", 18), Person("A", 18))list.forEach {if (it.name == "A") {println("Found")return@forEach}}println("not Found")
}

匿名函数

如果一个Lambda包含多个局部返回语句会变得笨重,此时可以使用匿名函数替代

class Person(val name: String, val age: Int)val list = listOf<Person>()
list.filter(fun(person): Boolean {return person.age < 30
})list.filter(fun(person) = person.age < 30)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/438833.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

座位预约|座位预约小程序|基于微信小程序的图书馆自习室座位预约管理系统设计与实现(源码+数据库+文档)

座位预约小程序目录 目录 基于微信小程序的图书馆自习室座位预约管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员服务端功能模块 2、学生微信端功能模块 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 …

js实现动漫拼图2.0版

比较与1.0版&#xff0c;2.0版就更像与华容道类似的拼图游戏&#xff0c;从头到尾都只能控制白色块移动&#xff0c;而且打乱拼图和求助的实现与1.0都不相同 文章目录 1 实现效果2 实现思路2.1 打乱拼图2.2 求助功能2.3 判赢 3 代码实现 js实现动漫拼图1.0版 https://blog.csdn…

网络协议与攻击模拟_10DHCP攻击与DHCP欺骗

一、DHCP的报文格式 Message type&#xff1a;消息类型&#xff08;1表示请求&#xff0c;2表示响应&#xff09;Hardware type&#xff1a;硬件类型Hardware address length&#xff1a;硬件地址长度Hops&#xff1a;DHCP报文经过中继的数目。Transaction ID&#xff1a;事务…

MySQL中使用percona-xtrabackup工具 三种备份及恢复 (超详细教程)

CSDN 成就一亿技术人&#xff01; 今天讲讲再MySQL中使用percona-xtrabackup这个开源工具来实现在线备份。 CSDN 成就一亿技术人&#xff01; 目录 介绍percona-xtrabackup 安装Percona 完整备份 备份流程 恢复流程 1.模拟文件损坏 2.滚回日志 3.恢复数据目录 4.授权…

C语言指针进阶(1)(超详细)

前言&#xff1a; 指针其实就是地址&#xff0c;而凡是存储在内存中的值都会有属于自己的地址&#xff0c;指针指向地址&#xff0c;这样我们就能通过指针间接操作变量。我们在指针初阶中介绍了指针的基本概念&#xff1a;如指针大小、野指针问题、指针间的关系运算等&#xff…

CTF CRYPTO 密码学-7

题目名称&#xff1a;敲击 题目描述&#xff1a; 让我们回到最开始的地方 0110011001101100011000010110011101111011011000110110010100110011011001010011010100110000001100100110001100101101001101000011100001100011001110010010110100110100011001000011010100110000…

贪吃蛇游戏的实现

一.技术要点: 贪吃蛇需要掌握: c语言函数,枚举,结构体,动态内存管理,预处理指令,链表,Win32 API等 二.Win32 API 1.Win32 API简介 windows可以帮应用程序卡其视窗,描绘图案,使用周边设备,,Win32 API就是windows32位平台上的应用程序编程接口 2.控制台程序 (1).使用cmd命令…

【爬坑】临时修复To connect to xxx insecurely, use `--no-check-certificate‘报错

解决方案&#xff1a;wget请求时跳过证书验证。 sudo vim /etc/wgetrc 插入一行&#xff1a; check_certificate off 重新运行wget命令即可。

Markdown(2篇文章学会Markdown第二篇

目录 1. 图片1.1 行内形式图片&#xff1a;\!\[Alt text]\(/path/to/img.jpg "Optional title")1.2 参考形式图片&#xff1a;\!\[内容]\[1] \[1]: image_url "alt 提示" 2. 列表2.1 无序列表&#xff1a;*、或-2.2 有序列表&#xff1a;数字接着一个英文…

Redis学习——高级篇④

Redis学习——高级篇④ Redis7高级之Redis与Mysql数据双写一致性工程案例&#xff08;四&#xff09; 4.1 MySQL主从复制原理4.2 canal 工作原理4.3 mySQL->canal->redis 双写一致性1.环境2.配置Mysql3.配置canal4. Canal客户端&#xff08;Java编写&#xff0…

双非本科准备秋招(10.2)—— JVM3:垃圾收集器

垃圾收集器 分为七种&#xff0c;如下&#xff1a; 从功能的角度分为 1、串行&#xff1a;Serial、Serial Old 2、吞吐量优先&#xff1a;Parallel Scavenge、Parallel Old 3、响应时间优先&#xff1a;CMS 吞吐量优先VS响应时间优先 吞吐量运行用户代码时间/(运行用户代码…

AI-数学-高中-11-指数函数

原作者视频&#xff1a;初等函数】3指数函数&#xff08;基础&#xff09;_哔哩哔哩_bilibili 指数函数、幂函数&#xff1a; 注意&#xff1a;分段函数注意看分界点。 注意&#xff1a;复合函数&#xff0c;采用换元法分解为外层和内层2个函数&#xff0c;先计算外层函数t和画…