Kotlin出现Conflicting declarations同名val声明冲突如何解决
Kotlin同作用域val解构声明冲突问题及解决方案
问题复现
我定义了如下data class和工具函数:
import kotlin.math.sin import kotlin.math.cos data class SinCosAngle(val sa: Float, val ca: Float) fun angleCalc(rad: Float): SinCosAngle { return SinCosAngle(sin(rad), cos(rad)) }
在业务代码中多次调用该函数,部分调用写在for循环内,部分写在同一外层作用域下:
// 循环1 for (i in 0..72) { val (sinAngle, cosAngle) = angleCalc(deg2rad(i * 5f - 90f)) // 绘制逻辑省略 canvas.drawLine(x1, y1, x2, y2, paint) } paint.strokeWidth = 3f // 循环2 for (i in 0..8) { val (sinAngle, cosAngle) = angleCalc(deg2rad(i * 45f - 90f)) // 绘制逻辑省略 canvas.drawLine(x1, y1, x2, y2, paint) } // 外层作用域调用1 val (sinAngle, cosAngle) = angleCalc(deg2rad(course - 90f)) // 绘制逻辑省略 // 外层作用域调用2 val (sinAngle, cosAngle) = angleCalc(deg2rad(course - 90f - angleArrow)) // 绘制逻辑省略 // 外层作用域调用3 val (sinAngle, cosAngle) = angleCalc(deg2rad(course - 90f + angleArrow)) // 绘制逻辑省略
报错信息
两个for循环内的同名解构变量运行正常,但外层作用域的三次调用触发编译报错:
Conflicting declarations: val sinAngle: Float, val sinAngle: Float
临时修复方案是给外层变量加数字后缀区分,但存在命名冗余的问题。
开发环境为Android Studio 2020.3.1 Patch 3。
原因说明
问题本质是Kotlin的变量作用域规则:
- for循环内部定义的val属于循环的局部作用域,每个循环的作用域独立,和外部作用域互不干扰,因此循环内的同名变量不会冲突
- 外层作用域下的三次解构声明都属于同一个顶层作用域,Kotlin不允许在同一作用域下重复声明同名的不可变变量(val),因此触发声明冲突报错
解决方案
方案1:独立作用域包裹(保留原变量名)
如果不需要跨代码块访问sinAngle、cosAngle变量,可以将每段调用逻辑用独立代码块包裹,每个代码块内部的变量仅在块内生效,不会和其他块冲突。Kotlin中可以用标准函数run快速创建独立作用域:
paint.strokeWidth = 2f paint.color = getColor(R.color.red) run { val (sinAngle, cosAngle) = angleCalc(deg2rad(course - 90f)) x2 = x + cosAngle * radiusCourse y2 = y + sinAngle * radiusCourse canvas.drawLine(x, y, x2, y2, paint) x1 = x + cosAngle * radius y1 = y + sinAngle * radius canvas.drawCircle(x1, y1, smallCircleRadius, paint) } run { val (sinAngle, cosAngle) = angleCalc(deg2rad(course - 90f - angleArrow)) x1 = x2 - cosAngle * radiusArrow y1 = y2 - sinAngle * radiusArrow canvas.drawLine(x1, y1, x2, y2, paint) } run { val (sinAngle, cosAngle) = angleCalc(deg2rad(course - 90f + angleArrow)) x1 = x2 - cosAngle * radiusArrow y1 = y2 - sinAngle * radiusArrow canvas.drawLine(x1, y1, x2, y2, paint) }
方案2:复用SinCosAngle变量(提问者最终采用)
直接声明一个可复用的SinCosAngle类型可变变量,每次调用函数直接给该变量赋值,通过属性访问取值,无需每次声明新的解构变量,完全避免命名冲突:
var sc: SinCosAngle paint.strokeWidth = 2f paint.color = getColor(R.color.red) // 计算主航线角度 sc = angleCalc(deg2rad(course - 90f)) x2 = x + sc.ca * radiusCourse y2 = y + sc.sa * radiusCourse canvas.drawLine(x, y, x2, y2, paint) x1 = x + sc.ca * radius y1 = y + sc.sa * radius canvas.drawCircle(x1, y1, smallCircleRadius, paint) // 计算箭头左侧角度 sc = angleCalc(deg2rad(course - 90f - angleArrow)) x1 = x2 - sc.ca * radiusArrow y1 = y2 - sc.sa * radiusArrow canvas.drawLine(x1, y1, x2, y2, paint) // 计算箭头右侧角度 sc = angleCalc(deg2rad(course - 90f + angleArrow)) x1 = x2 - sc.ca * radiusArrow y1 = y2 - sc.sa * radiusArrow canvas.drawLine(x1, y1, x2, y2, paint)
该方案代码更简洁,内存占用也更低,适合频繁调用该函数的场景。
内容的提问来源于stack exchange,提问作者Artyum
相关产品推荐
相关产品推荐

