Kotlin

news/2025/3/16 0:41:07/

高阶函数

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

所有函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:(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() {@Overridevoid 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 + iprintln("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 表达式

例子

实现接口

var onVideoStartCallBack: (() -> Unit)? = nullonVideoStartCallBack?.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 = 32city = "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 = 8080query(prepareRequest() + " to port $port")
}// 同样的代码如果用 let() 函数来写:
val letResult = service.let {it.port = 8080it.query(it.prepareRequest() + " to port ${it.port}")
}

it作为参数的好处
let 允许我们自定义参数名字,使可读性更强,如果倾向可读性可以选择 T.let

 


http://www.ppmy.cn/news/92843.html

相关文章

AI怎么把游戏变好玩?米哈游出手了

《原神》发布两年半后&#xff0c;游戏新贵米哈游终于出新&#xff0c;上线了《崩坏:星穹铁道》。新游戏的一大亮点是内置了一个“图生图”的AIGC工具&#xff0c;用户可上传任何图片&#xff0c;生成对应风格的游戏角色“三月七”。 广大玩家脑洞大开&#xff0c;短短一周时间…

解决方案: MySQL JSON字段匹配值忽略大小写不敏感匹配结果

问题背景 MySQL 5.7 数据库由于历史原因设置的字符集为utf8 &#xff0c;排序规则为utf8_general_ci , 表是新建的&#xff0c;默认字符集设置为ut8mb4,排序规则为 ut8mb4_general_ci, utf8_bin/utf8mb4&#xff1a;将字符串中的每一个字符以十六进制方式存储数据&#xff0c…

Python之网络编程

一、操作系统基础 操作系统&#xff1a;&#xff08;Operating System&#xff0c;简称OS&#xff09;是管理和控制计算机硬件与软件资源的计算机程序&#xff0c;是直接运行在“裸机”上的最基本的系统软件&#xff0c;任何其他软件都必须在操作系统的支持下才能运行。 注&a…

GPU云服务器Stable Diffusion搭建保姆级教程

搭建Stable Diffusion最大门槛就是GPU。许多人的电脑配置太低&#xff0c;根本无法搭建。或者即使搭建出来&#xff0c;但是跑图太慢。说多了不通过&#xff0c;看下图。 选择服务器 我选择的是境外GPU服务器&#xff0c;windows版本&#xff08;73.59元&#xff09;。linux会…

python 内置模块multiprocessing,进程

一、简介 进程是操作系统进行资源分配的基本单位&#xff0c;也就是说每启动一个进程&#xff0c;操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行&#xff1b;每个进程都是独立运行的&#xff0c;不互相干扰 注意&#xff1a;&#xff1a;进程锁和线程锁大同小异…

基于 SpringBoot + Redis 实现分布式锁

大家好&#xff0c;我是余数&#xff0c;这两天温习了下分布式锁&#xff0c;然后就顺便整理了这篇文章出来。文末附有源码链接&#xff0c;需要的朋友可以自取。 至于什么是分布式锁&#xff0c;这里不做赘述&#xff0c;不了解的可以自行去查阅资料。 文章目录 实现要点项目…

SVN安装教程详解:快速掌握SVN的安装和使用方法

本文为大家介绍了SVN的详细安装步骤&#xff0c;并提供了具体的命令行操作和TortoiseSVN的图形用户界面操作说明&#xff0c;帮助读者轻松掌握SVN的安装和使用方法。包括创建版本库&#xff0c;添加项目文件到版本库&#xff0c;创建工作副本&#xff0c;对文件进行修改和提交&…

[GreyCTF‘23] crypto部分

baby crypto 凯撒签到 whuo{squi4h_s1fx3h_v0h_co_i4b4T} grey{caes4r_c1ph3r_f0r_my_s4l4D} The Vault 这里只有一个check_keys函数&#xff0c;加密这块破不了&#xff0c;只要过了check_keys就行。 from hashlib import sha256 from Crypto.Util.number import long_to_…