2-2-20 快速掌握Kotlin-infix关键字

25 阅读3分钟

Kotlin中的infix关键字允许使用中缀表示法调用函数,使代码更简洁、可读性更高。

基本概念

infix关键字用于将函数标记为可以使用中缀表示法调用(省略点号和括号)。

使用条件:

  1. 必须是成员函数或扩展函数
  2. 必须只有一个参数
  3. 参数不能是可变参数(vararg)
  4. 参数不能有默认值

基本语法

infix fun 类型.函数名(参数: 参数类型): 返回类型 {
    // 函数体
}

示例

1. 创建自定义中缀函数

class Person(val name: String) {
    // 中缀函数
    infix fun marry(spouse: Person) {
        println("$name${spouse.name} 结婚了!")
    }
}

fun main() {
    val alice = Person("Alice")
    val bob = Person("Bob")
    
    // 使用中缀表示法
    alice marry bob  // 输出:Alice 和 Bob 结婚了!
    
    // 等价于普通调用
    alice.marry(bob)
}

2. 扩展函数中使用中缀

infix fun Int.add(x: Int): Int = this + x

infix fun String.concat(other: String): String = "$this $other"

fun main() {
    val result1 = 5 add 3  // 8
    val result2 = "Hello" concat "World"  // "Hello World"
    
    println(result1)
    println(result2)
}

3. DSL(领域特定语言)应用

infix fun String.shouldBe(expected: String) {
    if (this == expected) {
        println("测试通过:$this == $expected")
    } else {
        println("测试失败:$this != $expected")
    }
}

fun main() {
    "Kotlin" shouldBe "Kotlin"  // 测试通过
    "Java" shouldBe "Kotlin"    // 测试失败
}

Kotlin标准库中的中缀函数

Kotlin标准库已经提供了多个有用的中缀函数:

1. to 函数(创建Pair)

val pair = "name" to "John"
// 等价于
val pair2 = Pair("name", "John")

2. 区间操作

val range = 1..10
// 等价于
val range2 = 1.rangeTo(10)

val charRange = 'a'..'z'

3. 位运算

val flags = 0b0010
val result1 = flags shl 2  // 左移2位
val result2 = flags shr 1  // 右移1位
val result3 = flags and 0b0001  // 与运算

实际应用示例

1. 构建查询DSL

class Query {
    private val conditions = mutableListOf<String>()
    
    infix fun where(condition: String) {
        conditions.add(condition)
    }
    
    infix fun and(condition: String) {
        conditions.add("AND $condition")
    }
    
    infix fun or(condition: String) {
        conditions.add("OR $condition")
    }
    
    override fun toString(): String {
        return "SELECT * FROM users WHERE " + conditions.joinToString(" ")
    }
}

fun query(init: Query.() -> Unit): Query {
    val query = Query()
    query.init()
    return query
}

fun main() {
    val q = query {
        where "age > 18"
        and "country = 'US'"
        or "status = 'active'"
    }
    println(q)  // SELECT * FROM users WHERE age > 18 AND country = 'US' OR status = 'active'
}

2. 数学表达式

data class Vector(val x: Int, val y: Int) {
    infix fun dot(other: Vector): Int {
        return this.x * other.x + this.y * other.y
    }
    
    infix fun cross(other: Vector): Int {
        return this.x * other.y - this.y * other.x
    }
}

fun main() {
    val v1 = Vector(1, 2)
    val v2 = Vector(3, 4)
    
    val dotProduct = v1 dot v2  // 11
    val crossProduct = v1 cross v2  // -2
    
    println("点积: $dotProduct")
    println("叉积: $crossProduct")
}

3. 配置构建器

class Configuration {
    var host: String = "localhost"
    var port: Int = 8080
    var timeout: Int = 30
    
    override fun toString(): String {
        return "Configuration(host='$host', port=$port, timeout=$timeout)"
    }
}

infix fun String.toHostOf(config: Configuration) {
    config.host = this
}

infix fun Int.toPortOf(config: Configuration) {
    config.port = this
}

fun configure(block: Configuration.() -> Unit): Configuration {
    val config = Configuration()
    config.block()
    return config
}

fun main() {
    val config = configure {
        "api.example.com" toHostOf this
        443 toPortOf this
        timeout = 60
    }
    println(config)
}

注意事项

  1. 优先级:中缀函数调用比布尔操作符&&||isin检测等具有更低的优先级

    if (a && b isType c)  // 先执行 b isType c,再与a进行&&运算
    
  2. 可读性:虽然中缀表示法可以使代码更简洁,但过度使用可能会降低代码的可读性

  3. 清晰性:确保函数名和参数类型能清晰地表达意图

最佳实践

  1. 当函数表示两个对象之间的关系时使用中缀表示法
  2. 为DSL设计时特别有用
  3. 保持函数名简洁且具有描述性
  4. 避免在复杂表达式中过度使用中缀表示法

总结

infix关键字是Kotlin提供的一个强大特性,它:

  • 使代码更简洁、更易读
  • 特别适合创建DSL
  • 在数学表达式、集合操作等场景中非常有用
  • 需要谨慎使用,确保代码可读性

通过合理使用infix函数,你可以创建出更加优雅和表达性强的Kotlin代码。