kotlin - 如何在 Kotlin 中表达“在第一次调用时只赋值一次”?
问题描述
寻找一种自然的 Kotlin 方式,让其startTime
仅在特定位置且仅初始化一次。
以下幼稚的实现有两个问题:
- 它不是线程安全的
- 它没有表达“变量在 Item 实例的生命周期中被或将被分配一次”的事实
class Item {
var startTime: Instant?
fun start(){
if (startTime == null){
startTime = Instant.now()
}
// do stuff
}
}
我相信某种代表可能适用于此。换句话说,这段代码需要类似于lazy
变量的东西,但在第一次读取时没有初始化,而是仅在显式调用“touching”方法后发生。也许这些Wrap
电话可以给出可能实施的想法。
class Wrap<T>(
supp: () -> T
){
private var value: T? = null
private val lock = ReentrantLock()
fun get(){
return value
}
fun touch(){
lock.lock()
try{
if (value == null){
value = supp()
} else {
throw IllegalStateExecption("Duplicate init")
}
} finally{
lock.unlock()
}
}
}
解决方案
AtomicReference.compareAndSet
与自定义支持字段结合如何?
您可以使用私有设置器并确保类设置值的唯一位置来自start()
方法。
class Item(val value: Int) {
private val _startTime = AtomicReference(Instant.EPOCH)
var startTime: Instant?
get() = _startTime.get().takeIf { it != Instant.EPOCH }
private set(value) = check(_startTime.compareAndSet(Instant.EPOCH, value)) { "Duplicate set" }
fun start() {
startTime = Instant.now()
}
override fun toString() = "$value: $startTime"
}
fun main() = runBlocking {
val item1 = Item(1)
val item2 = Item(2)
println(Instant.now())
launch { println(item1); item1.start(); println(item1) }
launch { println(item1) }
delay(1000)
println(item2)
item2.start()
println(item2)
println(item2)
item2.start()
}
示例输出:
2021-07-14T08:20:27.546821Z
1: null
1: 2021-07-14T08:20:27.607365Z
1: 2021-07-14T08:20:27.607365Z
2: null
2: 2021-07-14T08:20:28.584114Z
2: 2021-07-14T08:20:28.584114Z
Exception in thread "main" java.lang.IllegalStateException: Duplicate set
推荐阅读
- javascript - 我试图知道类型脚本。但现在显示类型错误
- amazon-web-services - 调用 RunInstances 操作时出错 (InvalidSubnetID.NotFound):子网 ID 'subnet-xxxxxxx' 不存在
- python - 如何使用项目的 .pyc (pycache) 文件构建 docker
- jquery - 如何从序列化的输入字段中获取标签文本?
- c++ - 是否可以制作 constexpr 树?
- php - 在数据库中建立连接时出错。在 Wampp 本地主机上安装 Wordpress
- android - 是否可以在 google play 上使用新密钥上传 android 应用程序
- java - 无法为自定义编辑文本应用样式
- android - 是否有可能将android应用程序启动时间减少到几乎为零?(如 Instagram 和 WhatsApp 新更新)
- python - 在 Python 的其他模块中使用主模块中的实例