Kotlin学习之infix函数运用

本篇博客主要讲解Kotlin语言中infix函数的运用


什么是 infix 函数

Kotlin允许在不使用括号和点号的情况下调用函数,那么这种函数被称为 infix 函数。

系统 infix 函数示例

  • 系统方法

    1
    2
    3
    4
    5
    6
    7
     *	
    * Creates a tuple of type [Pair] from this and [that].
    *
    * This can be useful for creating [Map] literals with less noise, for example:
    * @sample samples.collections.Maps.Instantiation.mapFromPairs
    */
    public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
  • 相关示例

    1
    2
    3
    4
    5
    map(
    1 to "one",
    2 to "two",
    3 to "three"
    )

除了用于创建Pair <A,B>实例的to()函数之外,还有一些其他函数定义为中缀。
例如,各种数字类Byte,Short,Int和Long都定义了按位函数and()或or(),shl(),shr(),ushr()和xor(),从而允许更多可读的表达式:

1
2
3
4
val color = 0x123456
val red = (color and 0xff0000) shr 16
val green = (color and 0x00ff00) shr 8
val blue = (color and 0x0000ff) shr 0

编写自定义 infix 函数

通常,我们将要编写自己的infix方法。例如,当为我们的应用程序编写领域特定语言时,这些功能尤其有用,从而使DSL代码更具可读性。
一些Kotlin库已经使用此方法产生了很大的效果。

例如,mockito-kotlin库定义了一些infix函数-doAnswer,doReturn和doThrow-在定义模拟行为时使用。

创建infix 函数需要满足以下要求:

  • 它们必须是成员函数或扩展函数;
  • 它们必须只有一个参数;
  • 其参数不得接受可变数量的参数且不能有默认值。

请注意:中缀函数总是要求指定接收者与参数。当使用中缀表示法在当前接收者上调用方法时,需要显式使用 this;不能像常规方法调用那样省略。

1
2
3
4
5
6
7
8
9
class MyStringCollection {
infix fun add(s: String) { /* …… */ }

fun build() {
this add "abc" // 正确
add("abc") // 正确
add "abc" // 错误:必须指定接收者
}
}

示例如下:

1
2
3
4
5
6
7
8
9
class Assertion<T>(private val target: T) {
infix fun isEqualTo(other: T) {
Assert.assertEquals(other, target)
}

infix fun isDifferentFrom(other: T) {
Assert.assertNotEquals(other, target)
}
}
  • 这看起来很简单,而且与其他Kotlin代码没有什么不同。但是,infix关键字的存在使我们可以编写如下代码:
    1
    2
    3
    4
    val result = Assertion(5)
    result isEqualTo 5 // This passes
    result isEqualTo 6 // This fails the assertion
    result isDifferentFrom 5 // This also fails the assertion
    这是更清晰易读和易于理解的。

注意,也可以将infix函数编写为现有类的扩展方法。

这可能很强大,因为它使我们能够从其他地方(包括标准库)扩充现有的类,以满足我们的需求。
例如,让我们在字符串中添加一个函数,以拉出与给定正则表达式匹配的所有子字符串:

1
2
3
4
5
6
7
infix fun String.substringMatches(r: Regex) : List<String> {
return r.findAll(this)
.map { it.value }
.toList()
}
val matches = "a bc def" substringMatches ".*? ".toRegex()
Assert.assertEquals(listOf("a ", "bc "), matches)

其他

中缀函数与操作符的优先级

  • 中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo 操作符
    1
    2
    3
    1 shl 2 + 3  等价于         1 shl (2 + 3)
    0 until n * 2 等价于 0 until (n * 2)
    xs union ys as Set<*> 等价于 xs union (ys as Set<*>)
  • 中缀函数调用的优先级高于布尔操作符 && 与 ||、is- 与 in- 检测以及其他一些操作符。
    1
    2
    a && b xor c  等价于   a && (b xor c)
    a xor b in c 等价于 (a xor b) in c

相关参考