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

news2024/11/14 19:42:53

高阶函数

高阶函数以另一个函数作为参数或者返回值,其可用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)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MBtiles数据索引和服务发布】GeoServer改造Springboot番外系列二

xyz地图服务访问示例&#xff1a;http://192.168.1.240:8081/gmserver/raster/xyz/firstWP:Imagery-raster/{z}/{x}/{y}.jpg 访问示例如下&#xff1a; mbtiles目录结构 根据z&#xff0c;x&#xff0c;y获取对应mbtiles文件路径的工具方法 说明&#xff1a;重点是使用getMb…

堆宝塔

L2-1 堆宝塔 分数 25 作者 陈越 单位 浙江大学 堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小&#xff0c;按照从大到小的顺序堆起宝塔。但彩虹圈不一定是按照直径的大小顺序抓到的。聪明…

第六讲_JavaScript原型

JavaScript原型 1. 原型的概念2. 原型继承2.1 原型链 3. class类的原型对象 1. 原型的概念 原型是 JavaScript 对象相互继承特性的机制。 每个函数都有一个 prototype 属性&#xff0c;这个属性指向一个对象&#xff0c;这个对象称为原型对象。每个对象都有一个 [[Prototype]…

在线mockjson

在线mockjson体验地址 在调一个问题的时候&#xff0c;但是问题的数据可能并不能随着想到的场景就变化&#xff0c;譬如说又个数组长度的情况&#xff0c;可能默认的情况下是返回4个元素&#xff0c;但是想要返回为空的时候&#xff0c;如果联系服务给改一下&#xff0c;那么流…

Pyecharts炫酷热力图:参数详解与实战大揭秘

Pyecharts绘制多种炫酷热力图参数说明代码实战 引言 热力图在数据可视化中是一种强大的工具&#xff0c;可以直观地展示数据的分布情况和变化趋势。Pyecharts是一个基于Echarts的Python可视化库&#xff0c;提供了丰富的图表类型&#xff0c;包括热力图。在本文中&#xff0c…

防御保护---防火墙的NAT-easyip

文章目录 目录 文章目录 点击此处查看NAT技术详解 一.NAT分类 二.防火墙配置 一对多NAT配置 多对多NAT配置 练习 点击此处查看NAT技术详解 一.NAT分类 源IP-NAT(源IP与公网之间转换)&#xff1a;静态NAT&#xff0c;动态NAT&#xff0c;NAPT&#xff1b;使内网能够访问公网目…

Linux网络编程——网络初识

文章目录 1. 网络协议初识1.1 为什么要有网络协议1.2 协议分层 2. OSI七层模型3. TCP/IP五层&#xff08;或四层&#xff09;模型4. 网络传输基本流程5. 以太网通信 1. 网络协议初识 1.1 为什么要有网络协议 早期计算机是独立的&#xff0c;如果要进行数据交互&#xff0c;就…

安装好IntelliJ IDEA点击无反应,如何解决配置文件不一致导致的启动问题

在我们的开发生涯中&#xff0c;遇到IDE工具出现问题是在所难免的。最令人头疼的莫过于&#xff0c;你的IDEA(IntelliJ IDEA)无法启动&#xff0c;而且没有任何错误提示。这篇文章将详细讲解如何解决IntelliJ IDEA 2023.3.3版本启动失败的问题&#xff0c;这个问题可能也适用于…

【已解决】Centos安装不了podman问题(依赖无法安装)

今天安装podman一直安装不了&#xff0c;原因是containernetworking-plugins-1.1.1-1.el7.2.9.x86_64.rpm这个包因为网站的原因下载不了&#xff0c;不管是开启代理还是使用镜像源&#xff0c;都无法解决 最终是手动下载本地后上传至服务器解决&#xff0c;故把文件分享出来避…

【Web前端实操18】粘性定位——即固定顶层内容,可以继续滚动,但是顶层内容固定,不随着一起滚动

粘性定位 1、了解 可以被认为是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。粘性定位是指网页或移动应用程序中的一种特性,即当用户滚动页面时,某个元素能够保持在屏幕上特定位置不动,直到用户滚动到达一定位置或进行特定操作。这个特性可…

【C++】C++入门—— 引用

引用 1 前情提要2 概念剖析3 引用特性4 常引用5 使用场景5.1做参数5.2 做返回值 6 传值 传引用的效率比较7 引用与指针的差异Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读下一篇文章见 1 前情提要 在C语言中&#xff0c;我们往往会遇见复杂的指针&#xff08;如…

tcpdump在手机上的使用

首先手机得root才可以&#xff0c;主要分析手机与手机的通信协议 我使用的是一加9pro&#xff0c; root方法参考一加全能盒子、一加全能工具箱官方网站——大侠阿木 (daxiaamu.com)https://optool.daxiaamu.com/index.php tcpdump&#xff0c;要安装在/data/local/tmp下要arm6…

vite+vue3+ts项目上线docker 配置反向代理API

这次重点的坑是反向代理。 1。项目中配置代理&#xff0c;为了跨域请求数据 项目根目录中新建vite.config.ts文件 在文件中添加配置代理 注意&#xff1a;其中 /api 和target 的地址后面没有 / 2。在项目根目录中新建Httprequest.ts文件&#xff0c;引入axios&#xff0c;并…

矩阵键盘的使用

在定义局部变量时&#xff0c;一定要给该变量赋初值。在这个程序中&#xff0c;给按键按下的返回值变量 KeyNum 赋值为 20 。 矩阵键盘线行扫描法的学习链接&#xff1a;https://www.bilibili.com/video/BV1dv411z7Gd/?spm_id_from333.999.0.0&vd_sourceb91967c499b23106…

推荐系统|概要_基本概念

文章目录 基本概念曝光点击相关指标点击率点赞率/收藏率/转发率阅读完成率总结北极星指标 实验流程 基本概念 曝光 笔记出现在首页&#xff0c;叫作曝光。 点击 用户从笔记表面进入到相关笔记的详情页&#xff0c;叫作一次点击。 除此之外&#xff0c;用户的喜爱&#xff0c…

Mybatis-Plus扩展

7 MybatisX插件[扩展] 7.1 MybatisX插件介绍 MybatisX 是一款基于 IDEA 的快速开发插件&#xff0c;为效率而生。 安装方法&#xff1a;打开 IDEA&#xff0c;进入 File -> Settings -> Plugins -> Browse Repositories&#xff0c;输入 mybatisx 搜索并安装。 功…

基于Springboot的视频网站系统的设计与实现(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的视频网站系统的设计与实现&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…

Java入门高频考查基础知识8(腾讯18问1.5万字参考答案)

刷题专栏&#xff1a;http://t.csdnimg.cn/gvB6r Java 是一种广泛使用的面向对象编程语言&#xff0c;在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性&#xff0c;适用于多种应用场景&#xff0c;包括企业应用、移动应用、嵌入式系统等。 以下是几个面试技巧&…

Prometheus的pod部署

创建命名空间和账户以及集群账户 kubectl create ns monitor-sa kubectl create serviceaccount monitor -n monitor-sa kubectl create clusterrolebinding monitor-clusterrolebinding -n monitor-sa --clusterrolecluster-admin --serviceaccountmonitor-sa:monitor 创建…

电动汽车|不同类型电动汽车充电负荷蒙特卡洛法模拟研究(包括常规充电、快速充电、更换电池)

目录 主要内容 结果一览 常规充电 快速充电 更换电池 详实文档资料 下载链接 主要内容 本程序采用蒙特卡洛模拟了不同类型电动汽车充电负荷特点&#xff0c;包括常规充电、快速充电和更换电池三种。 充放电行为分为无序充电行为、受控充电行为和受控充放电行为…