Kotlin 中是否有任何糖可以在局部范围内仅初始化一次值,就像 C 中的静态局部变量一样?或者等效地,要在全局或类范围内拥有一个值,就像本地一样声明和使用?
我有一些 lambda 使用只需要初始化一次的数据:
enum class Type { GOOD, BAD, UGLY }
class Job(val state: Type, val foo: Foo) {}
// Given a list of Jobs, sort it in Type declaration order, then display it
fun displayJobs(jobs: List<Job>) {
textView.text = jobs.sortedBy { it ->
val order = Type.entries.withIndex().associate { (i, v) -> Pair(v, i) }
order.get(it.state)!!
}
}
我希望
order
位于 lambda 的范围内,因为这是它唯一使用的地方。但这意味着它会在“每次调用 lambda”时被重复计算。对于传递给 sort 的 lambda,这将是 n log n 函数调用和 Map 的副本!
我可以将其移出到函数作用域,但每次调用函数时它仍然会重新计算:
enum class Type { GOOD, BAD, UGLY }
fun displayJobs(jobs: List<Job>) {
val order = Type.entries.withIndex().associate { (i, v) -> Pair(v, i) }
textView.text = jobs.sortedBy { it ->
order.get(it.state)!!
}
}
我可以将
order
移动到某个顶级范围或伴生对象,并对其进行一次计算,但现在它距离其使用地点很远。
我可以将order
放入枚举类中,但我必须能够声明一个伴生对象:
inline fun <reified T : Enum<T>> enumOrder()
= enumValues<T>().withIndex().associate { (i, v) -> Pair(v, i) }
enum class Type { GOOD, BAD, UGLY;
companion object {
val order = enumOrder<Type>()
}
}
这个“缓存”顺序到一个val中,所以它不会被重新计算,并且它的访问方式并不令人惊讶:
fun displayJobs(jobs: List<Job>) {
textView.text = jobs.sortedBy { it ->
Type.order.get(it.state)!!
}
}
所以耶!!问题解决了!
除了...
对于我不拥有的枚举,我无法添加伴随对象,可以吗?
我可以将扩展属性附加到类中:
val KClass<Type>.order get() = enumValues<Type>().withIndex().associate { (i, v) -> Pair(v, i) }
textView.text = jobs.sortedBy { it ->
Type::class.order.get(it.state)!!
}
扩展属性不会分配内存,因此每次运行 lambda 时都会调用 get()
:我们又回到了开始的地方!
Type.entries.sortedBy { Type::class.order.get(it)!! }
调用该函数四次。
那么,Kotlin 是否提供了类似于 C 的
static
修饰符的东西?请注意,
lazy
并没有回答这个问题,在:fun displayJobs(jobs: List<Job>) {
textView.text = jobs.sortedBy { it ->
val order by lazy { Type.entries.withIndex().associate { (i, v) -> Pair(v, i) } }
order.get(it.state)!!
}
}
每次 order
调用 lambda 时都会(延迟)创建 sortedBy
,这又与要排序的元素数量成正比。跳过构建枚举对并直接使用序数属性怎么样?例如,
listOf(Job(UGLY, ..), Job(BAD, ..), Job(GOOD, ..))
.sortedBy { it.state.ordinal })
应该按顺序给你工作:好、坏和丑。