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>() {
            @Override
            public 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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue学习之nodejs环境搭建中的坑

Vue学习之nodejs环境搭建中的坑 1.nodejs安装后环境变量配置 &#xff08;1&#xff09;在nodejs安装目录下已有node_cache、node_global&#xff0c;如下&#xff1a; &#xff08;2&#xff09;在系统属性->环境变量中新建一个名为NODE_PATH的系统变量&#xff0c;值为n…

pytest框架的基本使用

1. 测试框架的作用 测试框架不关系用例的内容 它关心的是&#xff1a;用例编排和结果收集 2. pytest框架的特点 1. 适用于python语言 2. 用法符合python风格 3. 有丰富的生态 3. 安装pytest框架 1. 新建一个项目 2. 在项目终端窗口输入如下命令&#xff0c;用于安装py…

基于springboot网吧管理系统源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

Zygote的启动流程

在zygote进程对应的文件是app_main.cpp文件&#xff0c;在app_main.cpp文件的main()方法中先解析了init.rc中配置的参数并根据配置的参数设置zygote的状态。 在状态设置阶段主要做了&#xff1a; 设置进程名称为zygote通过startSystemServer true标示启动的是systemServer调…

6.s081 学习实验记录(三)system calls

文章目录 一、use gdb二、syscall&#xff1a;trace注意&#xff1a;实验代码&#xff1a;实验结果&#xff1a; 三、sysinfotips&#xff1a;实验代码实验结果 需要切换到 syscall 分支 一、use gdb 学习使用 gdb 调试 make qemu-gdb打开一个新的终端&#xff1a; gdb-mult…

换个思路快速上手UML和plantUML——时序图

上一章我们介绍了类图&#xff0c;我们很清楚&#xff0c;类图是从更加宏观的角度去梳理系统结构的&#xff0c;从类图中我们可以获取到类与类之间&#xff1a;继承&#xff0c;实现等关系信息&#xff0c;是宏观逻辑。下面我们继续换一个思路&#xff1a;作为一名软件工程结构…

【周赛】第382场周赛

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 从这一场&#xff08;第382场周赛&#xff09;周赛开始记录&#xff0c;目标是尽快达到准确快速AC前三道题&#xff0c;每场比赛…

贝莱德里程碑

作者&#xff1a;秦晋 见证奇迹的时刻。 1月27日&#xff0c;全球资产管理巨头贝莱德在美国证监会于1月10日审批通过比特币ETF的17天内&#xff0c;其比特币现货ETF资产管理规模已经突破20亿美元。持有比特币总数已达49952个。领跑其他9只比特币ETF机构参与者。不包括灰度GBTC&…

【JavaScript 漫游】专栏介绍

专栏介绍 本专栏旨在记录 JavaScript 核心语法&#xff0c;作为笔者日常学习和工作中的参考手册和代码示例仓库。 内容上力求覆盖 ES5、DOM、BOM 和 ES6 规范的所有内容。对于常用且重要的知识点&#xff0c;应该详细描述并附带有大量的代码示例。对于在工作场景中很少用到的…

数据结构——用Java实现二分搜索树

目录 一、树 二、二分搜索树 1.二叉树 2.二分搜索树 三、代码实现 1.树的构建 2.获取树中结点的个数 3.添加元素 4.查找元素 &#xff08;1&#xff09;查找元素是否存在 &#xff08;2&#xff09;查找最小元素 &#xff08;3&#xff09;查找最大元素 5.二分搜索…

算法39:统计全 1 子矩形(力扣1504)----单调栈

题目: 给你一个 m x n 的二进制矩阵 mat &#xff0c;请你返回有多少个 子矩形 的元素全部都是 1 。 示例 1&#xff1a; 输入&#xff1a;mat [[1,0,1],[1,1,0],[1,1,0]] 输出&#xff1a;13 解释&#xff1a; 有 6 个 1x1 的矩形。 有 2 个 1x2 的矩形。 有 3 个 2x1 的矩…

(2024|ICLR,MAD,真实数据与合成数据,自吞噬循环)自消耗生成模型变得疯狂

Self-Consuming Generative Models Go MAD 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 自吞噬生成模型 2.1 自吞噬过程 2.2 自吞噬过程的变体 2.3 自吞噬循环中的偏…

Advanced EFS Data Recovery:恢复 Windows NTFS 中 EFS 加密文件

Advanced EFS Data Recovery 数据恢复软件可以破解 NTFS 加密&#xff0c;并解密受 Windows 加密文件系统 &#xff08;EFS&#xff09; 保护的文件。 Advanced EFS Data Recovery 功能列表 通过破解加密文件系统 &#xff08;EFS&#xff09; 来解除 NTFS 加密 解密转移到另…

Java面试题(11)

59.说一说springMVC的运行流程 1. 用户向服务器发送请求&#xff0c;请求被 Spring 前端控制 Servelt DispatcherServlet 捕获&#xff1b; 2. DispatcherServlet 对请求 URL 进行解析&#xff0c;得到请求资源标识符&#xff08;URI&#xff09;。然后根据该 URI&#xff0c;…

STM32 E18-D80NK红外避障传感器

E18-D80NK-N是一款红外光电传感器&#xff0c;它同时具备发射和接收功能。通过对发射光进行调制后发出&#xff0c;并通过接收头对反射光进行解调输出。 E18-D80NK-N采用了透镜来增强传感器的性能&#xff0c;使其能够检测更远的距离。根据红外光的特性&#xff0c;不同颜色的…

VitePress-04-文档中的表情符号的使用

说明 vitepress 的文档中是支持使用表情符号的&#xff0c;像 &#x1f602; 等常用的表情都是支持的。 本文就来介绍它的使用方式。 使用语法 语法 &#xff1a; :表情名称: 例如 &#xff1a; :joy: &#x1f602; 使用案例代码 # 体会【表情】的基本使用 > hello world …

22.Lambda 表达式

Lambda 表达式 1. 概况2. 函数式接口3. 格式3.1 完整格式3.2 省略格式 4. 代码示例5. 输出结果6. 注意事项 学习Lambda表达式之前最好先学会 匿名内部类 具体信息请查看 API 帮助文档 1. 概况 Lambda 表达式是一种在编程中用来表示匿名函数的简洁语法。它是基于函数式编程风格…

2024.1.27每日一题

LeetCode 最大合金数 2861. 最大合金数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 假设你是一家合金制造公司的老板&#xff0c;你的公司使用多种金属来制造合金。现在共有 n 种不同类型的金属可以使用&#xff0c;并且你可以使用 k 台机器来制造合金。每台机器都需…

SpringBoot之JWT登录

JWT JSON Web Token&#xff08;JSON Web令牌&#xff09; 是一个开放标准(rfc7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任&#xff0c;因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法…

嵌入式学习第十三天

9.指针: &#xff08;1&#xff09;const指针 const 关键字 常量(只读) 1.const int *p; 2.int const *p; 1和2是等价的 const修饰 *p,指针变量p的值可以改变,但不能利用指针修改指向空间中的值 3.int *const p; const修饰 p,指针变量p的值不能改变…