一文带你了解 Kotlin infix 函数的基本用法和使用场景

infix 是 Kotlin 中的一个特殊关键字,它允许我们以更自然、更可读的方式调用只有一个参数的函数。这种函数调用方式去掉了传统的点号和括号,使代码更加简洁流畅。

一、基本概念与语法

1.1 什么是 infix 函数

infix 函数是满足以下条件的成员函数或扩展函数:

  1. 必须只有一个参数
  2. 不能接受可变参数
  3. 不能有默认参数值

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 限制与陷阱

  1. 不能与保留关键字冲突
kotlin 复制代码
// 错误:is 是保留关键字
// infix fun String.is(other: String) = this == other

// 解决方案:使用反引号转义
infix fun String.`is`(other: String) = this == other
  1. 避免过度使用
kotlin 复制代码
// 不推荐 - 可读性差
val result = data process filter apply transformation

// 推荐 - 适当使用传统调用方式
val result = data.process().filter().apply(transformation)
  1. 类型安全
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()

八、最佳实践

  1. 命名原则

    • 使用动词-名词或名词-动词形式(如 user assignTo project
    • 避免使用过于通用的名称(如 domake
  2. 适用场景

    • 自然语言风格的 DSL
    • 数学/逻辑表达式
    • 测试断言
    • 简单的二元关系表达
  3. 避免场景

    • 复杂逻辑(超过简单操作)
    • 需要多个参数的函数
    • 性能关键路径(infix 有轻微性能开销)
  4. 文档规范

    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 函数是一个强大的语法糖,它:

  1. 提高了特定场景下的代码可读性
  2. 支持创建流畅的 DSL
  3. 与扩展函数结合能极大增强表达能力

使用时应当:

  • 保持函数功能简单明确
  • 注意优先级规则
  • 避免过度使用导致代码难以理解
  • 为 Java 调用者提供替代方案

合理使用 infix 函数可以让你的 Kotlin 代码更加简洁优雅,特别是在构建领域特定语言(DSL)和测试框架时非常有用。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 委托与扩展函数------新手入门
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
  6. Kotlin 扩展方法(Extension Functions)使用详解
  7. Kotlin 中 == 和 === 的区别
  8. Kotlin 操作符与集合/数组方法详解------新手指南
  9. Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
  10. Kotlin Result 类型扩展详解 ------ 新手使用指南
相关推荐
_祝你今天愉快1 分钟前
深入剖析Java中ThreadLocal原理
android
张力尹1 小时前
谈谈 kotlin 和 java 中的锁!你是不是在协程中使用 synchronized?
android
流浪汉kylin1 小时前
Android 斜切图片
android
PuddingSama2 小时前
Android 视图转换工具 Matrix
android·前端·面试
RichardLai882 小时前
[Flutter学习之Dart基础] - 控制语句
android·flutter
archko2 小时前
compose map 源码解析
android
大熊的瓜地2 小时前
从零开始写android 的智能指针
android
甜辣小悦羊3 小时前
Android Studio 的安装教程
android·ide·android studio
louisgeek3 小时前
Android Intent
android
树獭非懒3 小时前
Android重学笔记|别再滥用广播了
android·客户端