1. 概述
in
和 !in
是 Kotlin 提供的两个常用操作符。本文将深入讲解它们的常见用法,并通过实际示例演示如何对 in
操作符进行重载,以提升代码可读性和表达力。
掌握这些技巧不仅能写出更简洁的逻辑判断,还能在设计领域模型时提供更强的语义表达能力——这在实际开发中是个实用的小技能,用好了能让同事眼前一亮 ✅
2. in 与 !in 操作符简介
in
和 !in
是二元操作符,最常见的用途有两个场景:
- 在
for
循环中遍历集合(如for (item in list)
) - 判断某个元素是否存在于容器中
本文重点讨论第二种用法:成员存在性检查(containment check)
核心机制
in
和 !in
操作符底层依赖于 contains()
方法:
x in y
等价于y.contains(x)
x !in y
等价于!y.contains(x)
这意味着只要一个类型实现了 contains()
方法,它就能天然支持 in
和 !in
操作符 ❗
⚠️ 注意:虽然我们可以在
for
循环中使用in
,但它实际上是基于iterator()
的实现,和本文讨论的contains()
无关,此处不再展开。
为了验证效果,后续示例统一使用单元测试断言方式展示结果,便于理解。
3. 常见使用场景
3.1 集合与数组
集合(Collection)和数组是最典型的容器类型,天然支持 in
/ !in
:
val mySet = setOf("a", "b", "c", "d", "e")
assertTrue { "a" in mySet }
assertTrue { "x" !in mySet }
数组同样适用:
val myArray = arrayOf("a", "b", "c", "d", "e")
assertTrue { "a" in myArray }
assertTrue { "x" !in myArray }
✅ 小贴士:Kotlin 对数组的支持是通过扩展函数实现的,不需要手动实现。
3.2 区间(Range)
Kotlin 内建的 Range
类型默认支持 in
操作符,非常适合做数值范围判断:
val myRange = 1..100
assertTrue { 42 in myRange }
assertTrue { 777 !in myRange }
这种写法比手写 if (x >= 1 && x <= 100)
更直观,也更安全,尤其在处理日期、年龄、分数等场景时非常实用。
3.3 字符串
字符串也可以使用 in
来判断子串或字符是否存在:
val myString = "a b c d e"
assertTrue { "a b" in myString } // 子串匹配
assertTrue { 'c' in myString } // 字符匹配
assertTrue { "X" !in myString }
底层调用的是 String.contains()
方法,行为符合直觉。
4. in 操作符重载实战
Kotlin 支持操作符重载(operator overloading),我们可以通过定义带有 operator
关键字的 contains
函数,让自定义类型也支持 in
操作符。
语法要求:
- 成员函数 或 扩展函数
- 使用
operator
修饰 - 名称为
contains
下面通过两个典型例子说明如何合理使用这一特性。
4.1 球员与比赛场景
假设我们有如下数据类:
data class Player(val name: String, val rank: Int)
data class Team(val name: String, val players: Set<Player>)
data class Match(val place: String, val teams: Pair<Team, Team>)
初始化一些测试数据:
val eric = Player("Eric", 8)
val kai = Player("Kai", 7)
val teamA = Team("Team A", setOf(eric, kai))
val kevin = Player("Kevin", 6)
val saajan = Player("Saajan", 11)
val teamB = Team("Team B", setOf(kevin, saajan))
val tom = Player("Tom", 1)
val jerry = Player("Jerry", 9)
val teamC = Team("Team C", setOf(tom, jerry))
val match1 = Match("Frankfurt", teamA to teamC)
val match2 = Match("Hamburg", teamB to teamC)
val match3 = Match("Berlin", teamA to teamB)
现在想判断某位球员是否参与了某场比赛。常规做法需要层层解构:
// 老老实实写判断
fun isPlayerInMatch(player: Player, match: Match): Boolean {
return player in match.teams.first.players || player in match.teams.second.players
}
但如果我们能让 in
直接支持 Match
类型,代码就会优雅很多:
operator fun Match.contains(player: Player): Boolean {
return teams.toList().any { player in it.players }
}
重载后即可直接使用:
assertTrue { eric in match1 }
assertTrue { saajan !in match1 }
assertFalse { kai in match2 }
assertTrue { tom !in match3 }
✅ 效果:语义清晰,接近自然语言表达,别人一眼就能看懂“eric 是否参加了 match1”。
4.2 正则匹配模式识别
我们有三个预定义的正则表达式:
val pattern33 = Regex("^[a-z]{3} - [a-z]{3}$")
val pattern34 = Regex("^[a-z]{3} - [a-z]{4}$")
val pattern44 = Regex("^[a-z]{4} - [a-z]{4}$")
目标是根据输入字符串判断其符合哪种模式。
方案一:if-else 判断
fun determinePattern1(input: String): String {
return if (input.matches(pattern33)) {
"3-3"
} else if (input.matches(pattern34)) {
"3-4"
} else if (input.matches(pattern44)) {
"4-4"
} else {
"none"
}
}
方案二:when 表达式优化
fun determinePattern2(input: String): String {
return when {
input.matches(pattern33) -> "3-3"
input.matches(pattern34) -> "3-4"
input.matches(pattern44) -> "4-4"
else -> "none"
}
}
虽然结构更清晰,但仍存在重复调用 matches()
的问题。
方案三:重载 in 操作符
通过为 Regex
添加扩展函数,让其支持 in
:
operator fun Regex.contains(input: String): Boolean {
return this.matches(input)
}
然后就可以在 when
中直接使用 in
:
fun determinePattern(input: String): String {
return when (input) {
in pattern33 -> "3-3"
in pattern34 -> "3-4"
in pattern44 -> "4-4"
else -> "none"
}
}
✅ 最终效果:逻辑清晰、无冗余调用、可读性强,堪称 Kotlin 风格典范。
验证测试:
assertEquals("3-3", determinePattern("abc - xyz"))
assertEquals("3-4", determinePattern("top - down"))
assertEquals("4-4", determinePattern("good - nice"))
assertEquals("none", determinePattern("1234 - 4321"))
⚠️ 踩坑提醒:重载操作符要适度!不要为了炫技而滥用,否则会让团队新人难以理解。建议只在语义明确、能显著提升可读性的场景下使用。
5. 总结
本文系统介绍了 Kotlin 中 in
和 !in
操作符的核心机制与典型应用:
- ✅
in
本质是contains()
方法的语法糖 - ✅ 常用于集合、数组、区间、字符串的存在性检查
- ✅ 可通过 operator + contains 实现自定义类型的 in 支持
- ✅ 合理重载能大幅提升代码可读性,尤其是在领域建模和 DSL 设计中
完整示例代码已托管至 GitHub:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-collections-5
掌握这些技巧后,下次写条件判断时不妨想想:能不能用 in
让代码更优雅一点?💡