infix
是 Kotlin 中的一个特殊关键字,它允许我们以更自然、更可读的方式调用只有一个参数的函数。这种函数调用方式去掉了传统的点号和括号,使代码更加简洁流畅。
一、基本概念与语法
1.1 什么是 infix
函数
infix
函数是满足以下条件的成员函数或扩展函数:
- 必须只有一个参数
- 不能接受可变参数
- 不能有默认参数值
1.2 基本语法
kotlin
infix fun 接收者类型.函数名(参数: 参数类型): 返回类型 {
// 函数体
}
二、基本用法示例
2.1 自定义 infix
函数
kotlin
class Person(val name: String) {
infix fun say(message: String) {
println("$name says: $message")
}
}
fun main() {
val alice = Person("Alice")
// 常规调用
alice.say("Hello") // 输出: Alice says: Hello
// infix 调用
alice say "Hi" // 输出: Alice says: Hi
}
2.2 扩展函数作为 infix
kotlin
infix fun Int.add(other: Int): Int = this + other
fun main() {
println(5 add 3) // 输出: 8
}
三、标准库中的 infix
函数
Kotlin 标准库中已经定义了一些有用的 infix
函数:
3.1 to
函数(创建 Pair)
kotlin
fun main() {
val pair1 = "key" to "value" // 等价于 Pair("key", "value")
val pair2 = 1 to 2
println(pair1) // 输出: (key, value)
println(pair2) // 输出: (1, 2)
}
3.2 范围操作
kotlin
fun main() {
val range1 = 1 until 10 // 1..9
val range2 = 'a'..'z'
println(5 in range1) // true
println('A' in range2) // false
}
3.3 位运算
kotlin
fun main() {
val flags = 0b1010
val mask = 0b1100
println(flags and mask) // 8 (0b1000)
println(flags or mask) // 14 (0b1110)
println(flags xor mask) // 6 (0b0110)
}
四、实用场景
4.1 DSL 构建
kotlin
class Html {
private val children = mutableListOf<Any>()
infix fun String.`class`(value: String) {
children.add("$this class=\"$value\"")
}
override fun toString(): String = children.joinToString("\n")
}
fun html(init: Html.() -> Unit): Html {
val html = Html()
html.init()
return html
}
fun main() {
val page = html {
"div" `class` "container"
"span" `class` "text-primary"
}
println(page)
/* 输出:
div class="container"
span class="text-primary"
*/
}
4.2 测试断言
kotlin
infix fun <T> T.shouldBe(expected: T) {
if (this != expected) {
throw AssertionError("Expected $expected but was $this")
}
}
fun main() {
val result = 3 * 7
result shouldBe 21 // 通过
// result shouldBe 20 // 抛出 AssertionError
}
4.3 数学表达式
kotlin
infix fun Int.pow(exponent: Int): Int {
return when {
exponent < 0 -> throw IllegalArgumentException("Negative exponent")
exponent == 0 -> 1
else -> this * this.pow(exponent - 1)
}
}
fun main() {
println(2 pow 10) // 1024
}
五、注意事项
5.1 优先级规则
infix
函数调用的优先级低于算术运算符,但高于布尔运算符和比较运算符:
kotlin
fun main() {
println(1 + 2 pow 3) // 等价于 1 + (2 pow 3) = 9
println(2 pow 3 + 1) // 等价于 (2 pow 3) + 1 = 9
}
5.2 与操作符重载的区别
特性 | infix 函数 | 操作符重载 |
---|---|---|
语法 | 自定义名称 | 固定操作符集 |
参数数量 | 必须一个参数 | 根据操作符决定 |
调用方式 | 空格分隔 | 符号操作符 |
使用场景 | 提高可读性 | 数学/逻辑运算 |
5.3 限制与陷阱
- 不能与保留关键字冲突:
kotlin
// 错误:is 是保留关键字
// infix fun String.is(other: String) = this == other
// 解决方案:使用反引号转义
infix fun String.`is`(other: String) = this == other
- 避免过度使用:
kotlin
// 不推荐 - 可读性差
val result = data process filter apply transformation
// 推荐 - 适当使用传统调用方式
val result = data.process().filter().apply(transformation)
- 类型安全:
kotlin
infix fun String.repeat(count: Int): String = this.repeat(count)
fun main() {
// 编译错误:类型不匹配
// "a" repeat "3"
// 正确
"a" repeat 3
}
六、高级技巧
6.1 结合扩展属性
kotlin
val Int.ms get() = this
val Int.seconds get() = this * 1000
infix fun Int.after(unit: Int) = System.currentTimeMillis() + this * unit
fun main() {
val futureTime = 10 seconds after System.currentTimeMillis()
println("Future timestamp: $futureTime")
}
6.2 链式调用
kotlin
class Query {
private val conditions = mutableListOf<String>()
infix fun where(condition: String) = apply { conditions.add(condition) }
override fun toString(): String = conditions.joinToString(" AND ")
}
fun main() {
val query = Query() where "age > 18" where "name = 'Alice'"
println(query) // 输出: age > 18 AND name = 'Alice'
}
6.3 类型安全构建器
kotlin
class Route {
private val path = mutableListOf<String>()
infix fun String.to(next: String) = apply {
path.add(this)
path.add(next)
}
fun build(): String = path.joinToString(" -> ")
}
fun main() {
val route = Route() "Home" to "Products" to "Cart" to "Checkout"
println(route.build()) // 输出: Home -> Products -> Cart -> Checkout
}
七、与 Java 互操作
7.1 Java 调用 Kotlin infix
函数
kotlin
// ---- Kotlin ----
class Calculator {
infix fun add(x: Int) = this + x
}
java
// ---- Java ----
public class JavaClient {
public static void main(String[] args) {
Calculator calc = new Calculator();
// 只能使用常规调用方式
int result = calc.add(5);
}
}
7.2 替代方案
对于需要 Java 和 Kotlin 都友好的 API:
kotlin
class Api {
// 常规函数
fun connect(host: String) { /*...*/ }
// infix 别名
infix fun to(host: String) = connect(host)
}
// Kotlin 中两种方式都可以
Api().to("example.com")
Api().connect("example.com")
// Java 中只能使用 connect()
八、最佳实践
-
命名原则:
- 使用动词-名词或名词-动词形式(如
user assignTo project
) - 避免使用过于通用的名称(如
do
、make
)
- 使用动词-名词或名词-动词形式(如
-
适用场景:
- 自然语言风格的 DSL
- 数学/逻辑表达式
- 测试断言
- 简单的二元关系表达
-
避免场景:
- 复杂逻辑(超过简单操作)
- 需要多个参数的函数
- 性能关键路径(infix 有轻微性能开销)
-
文档规范:
kotlin/** * Connects this user to the specified project * @param project the target project * @return the assignment record */ infix fun User.assignTo(project: Project): Assignment
九、总结
Kotlin 的 infix
函数是一个强大的语法糖,它:
- 提高了特定场景下的代码可读性
- 支持创建流畅的 DSL
- 与扩展函数结合能极大增强表达能力
使用时应当:
- 保持函数功能简单明确
- 注意优先级规则
- 避免过度使用导致代码难以理解
- 为 Java 调用者提供替代方案
合理使用 infix 函数可以让你的 Kotlin 代码更加简洁优雅,特别是在构建领域特定语言(DSL)和测试框架时非常有用。
更多分享
- 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
- 一文带你吃透Kotlin协程的launch()和async()的区别
- Kotlin 委托与扩展函数------新手入门
- Kotlin 作用域函数(let、run、with、apply、also)的使用指南
- 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
- Kotlin 扩展方法(Extension Functions)使用详解
- Kotlin 中 == 和 === 的区别
- Kotlin 操作符与集合/数组方法详解------新手指南
- Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
- Kotlin Result 类型扩展详解 ------ 新手使用指南