我们已经多次使用过 A to B 这样的语法结构,包括 Kotlin 自带的 mapOf()
函数,这种语法可读性高,相比调用一个函数,它更接近于使用英语的语法来编写。
to
并不是 Kotlin 语言中的一个关键字,之所以我们能使用 A to B 这种语法结构,是因为 Kotlin 提供了一种高级语法糖特性:infix
函数,它只是把编程语言函数调用的语法规则调整了一下,比如 A to B 这样额写法,实际上等价于 A.to(B) 的写法。
下面通过两个具体的例子来学习一下 infix
函数的用法。
一、简单例子
String 类有一个 startsWith() 函数,用于判断一个字符串是否以某个指定参数开头,比如下面的这段代码判断结果一定会是 true:
if ("Hello Kotlin".startsWith("Hello")) {
// 处理具体逻辑
}
借助 infix
函数,可以使用一种更具可读性的语法来表达这段代码:
// String类扩展函数,给String添加beginsWith函数,通过调用startsWith实现
infix fun String.beginsWith(prefix: String) = startsWith(prefix)
加上 infix
关键字之后,beginsWith() 函数就变成了一个 infix
函数,这样除了传统的函数调用方法之外,还可以使用一种特殊的语法糖格式调用 beginsWith() 函数,如下所示:
// infix允许我们将调用函数的小数点、括号等相关语法去掉
// 从而使用一种更接近英语的语法来编写程序
if ("Hello Kotlin" beginsWith "Hello") {
// 处理具体逻辑
}
由于 infix
函数的特殊性,有两个比较严格的限制:首先,infix
函数不能定义成顶层函数,它必须是某个类的成员函数,可以使用扩展函数将它定义到某个类当中;其次,infix
函数必须且只能接收一个参数,至于参数类型是没有限制的。
二、复杂例子
这里有一个集合,如果想要判断集合中是否包含某个指定元素,一般可以这样写:
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
if (list.contains("Banana")) {
// 处理具体逻辑
}
使用 infix
函数让这段代码更具有可读性:
infix fun <T> Collection.<T>.has(element: T) = contains(element)
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
if (list has "Banana")) {
// 处理具体逻辑
}
三、模仿 to() 函数实现
先看一下 to()
函数的源码:
public infix fun <A,B> A.to(that: B): Pair<A,B> = Pair(this,that)
可以看到这里使用定义泛型函数的方式将 to()
函数定义到了 A 类型下,并且接收一个 B 类型的参数,因此 A 和 B 可以是两种不同类型的泛型,也就使得我们可以构建出字符串 to
整形这样的键值对。
to() 函数的具体实现也非常简单,就是创建并返回了一个 Pair 对象,也就是说 A to B 这样的语法结构实际上得到的是一个包含 A、B 数据的 Pair 对象,而 mapOf() 函数实际上接收的正式一个 Pair 类型的可变参数列表。
下面模仿 to() 函数实现我们自己的一个键值对函数:
infix fun <A,B> A.with(that: B): Pair<A,B> = Pair(this, that)
val map = mapOf("Apple" with 1, "Banana" with 2, "Organge" with 3, "Pear" with 4)