kotlin - Kotlin "assign value exactly once on the first call"如何表达?

标签 kotlin design-patterns delegates lazy-evaluation

寻找一种自然的 Kotlin 方法,让 startTime 仅在特定位置初始化一次。

以下简单的实现有两个问题:

  1. 它不是线程安全的
  2. 它没有表达“变量在 Item 实例的生命周期中曾经或将被分配一次”的事实

class Item {
    var startTime: Instant?

    fun start(){
        if (startTime == null){
            startTime = Instant.now()
        }

        // do stuff
    }
}

我相信某种委托(delegate)可以适用于此。换句话说,这段代码需要类似于惰性变量的东西,但在第一次读取时没有初始化,而是仅在显式调用“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怎么样?与定制backing field

您可以使用私有(private) setter ,并确保类设置值的唯一位置是来自 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

关于kotlin - Kotlin "assign value exactly once on the first call"如何表达?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68372363/

相关文章:

android - Android-每当显示 Activity 时,如何自动显示输​​入法选择器?

web-services - 连接池的良好设计模式是什么?

delegates - 使用 Ninject 进行惰性泛型委托(delegate)初始化

javascript - 使用 module.export 和 require 的 JavaScript 单例?

php - 设计模式: How to create database object/connection only when needed?

iOS Swift : Closures (Callbacks) versus Delegates, 什么时候使用哪个?

ios - 如何将数据从父 View Controller 传递到 subview Controller ?

Kotlin 类型安全构建器

Java 泛型到 Kotlin 泛型。从方法返回泛型

kotlin - 使用 groupBy/groupingBy/aggregate 并行求和到较小的桶中?