Kotlin IDE提示移除冗余let调用后程序语义改变问题咨询
问题成因解释
核心原因是 Kotlin 运算符的等价转换规则,以及有副作用函数的求值顺序差异:
- Kotlin 中
a in b运算符会被等价转换为b.contains(a),执行时会优先计算接收者b的值,再计算参数a的值,最终调用contains方法返回结果。 - 你用到的
read()是带副作用的函数:每次调用都会消耗一行输入流,调用顺序直接决定了取到的输入值。
带let的原始代码执行逻辑
fun main() = read().let { print(it in read()..read()) }的执行顺序完全符合你的预期:
- 先执行
let的接收者表达式read(),读取第一个输入20,存入lambda参数it(此时it是固定值,不会再触发输入读取) - 执行lambda内的表达式
it in read()..read(),先计算区间(in的接收者):- 第一个
read()读取第二个输入10 - 第二个
read()读取第三个输入30 - 生成有效区间
10..30
- 第一个
- 调用
10..30.contains(20),返回结果true
移除let后的代码执行逻辑
IDE的优化提示是错误的:它没有识别到read()是带副作用的函数,误以为let只是做了无意义的值传递,移除后实际执行顺序完全改变:fun main() = print(read() in read()..read())等价于print( (read()..read()).contains(read()) ),执行顺序为:
- 先计算
contains的接收者区间read()..read():- 第一个
read()读取第一个输入20作为区间左端点 - 第二个
read()读取第二个输入10作为区间右端点 - 生成左端点大于右端点的空区间
20..10(Kotlin的IntRange要求左<=右才会包含元素,否则是空区间)
- 第一个
- 再计算
contains的参数read(),读取第三个输入30 - 调用空区间的
contains(30),必然返回结果false
修复建议
不要依赖运算符优先级控制带副作用函数的执行顺序,显式给三个输入赋值变量再做判断,可读性和正确性都更有保障:
fun main() { val target = read() val rangeStart = read() val rangeEnd = read() print(target in rangeStart..rangeEnd) } fun read() = readLine()!!.toInt()
内容的提问来源于stack exchange,提问作者Salim Mazari Boufares
相关产品推荐
相关产品推荐

