Kotlin 异步初始化值

发布于:2025-05-14 ⋅ 阅读:(55) ⋅ 点赞:(0)

在一个类初始化的时候或者方法执行的时候,总有一些值是需要的但是不是立即需要的,并且在需要的时候需要阻塞流程来等待值的计算,这时候异步的形式创建这个值是毋庸置疑最好的选择。

为了更好的创建值需要使用 Kotlin 的协程来创建,因为协程足够轻量。那么要设置这个功能需要几个点

  1. 使用委托方式创建,这样可以和正常的使用计算结果的值一样使用
  2. 使用 kotlin 的协程异步进行创建
  3. 如果需要的时候值还未产生,需要等待结果产生
  4. 结果出现后,需要将值进行缓存,以便多次的方位该值
  5. 如果负责计算值的委托被赋值了新值,需要停止计算并将新值进行缓存
/**
 * 异步创建一个值,并在使用的时候等待其生产完成
 * 注意,不能生产空值
 * 例如:异步创建值,并在需要使用时等待其等待生产完成,如果已经生产完成就直接返回
 */
class AsyncValue<T : Any>(
    dispatcher: CoroutineDispatcher = Dispatchers.Default,
    private val producer: suspend () -> T
) {
    private var result: T? = null
    private var exception: Throwable? = null
    private var finished = false
    private val production: Deferred<T?> = async(dispatcher) {
        return@async invoke().apply {
            finished = true
            result = this
        }
    }

    private suspend fun invoke(): T? = try {
        producer()
    } catch (e: Exception) {
        exception = e
        null
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        if (!finished) production.cancel()
        result = value
        finished = true
    }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        exception?.let { throw it }
        return if (finished) {
            result ?: throw IllegalStateException("The value for ${property.name} is null")
        } else {
            runBlocking { await() }
        }
    }

    /**
     * 获取值,如果值还没有初始化完成就返回 null,或者出现异常了也返回 null
     */
    fun getOrNull(): T? {
        return result
    }

    /**
     * 获取值,如果值还没有初始化则抛出异常,如果出现异常了也抛出异常
     */
    fun getOrThrow(): T {
        exception?.let { throw it }
        return result ?: throw IllegalStateException("The value is null")
    }

    /**
     * 获取值,如果值不存在则等待初始化,如果出现异常了也抛出异常
     */
    suspend fun getAwaitOrThrow(): T {
        exception?.let { throw it }
        return await()
    }

    /**
     * 获取值,如果值不存在则等待初始化,如果出现异常了就返回 null
     */
    suspend fun getAwaitOrNull(): T? {
        return kotlin.runCatching { await() }.getOrNull()
    }

    private suspend fun await(): T {
        if (!finished) {
            production.await()
        }
        exception?.let { throw it }
        return result ?: throw IllegalStateException("The value is null")
    }
}

使用测试

val image: BufferedImage by AsyncValue {
     ImageIO.read(File("E:\\仓库\\study\\散图\\F0FadEKaEAAm9_m.jpg"))
}
// 其他逻辑
//...
// 使用异步创建的结果
println(image)

该程序例子是不能生产 Null 如果生产结果可能包含 Null 可以使用以下程序。否则直接返回 Null 会抛出异常

val optional: Optional<String> by AsyncValue {
    Optional.ofNullable(null)
}

网站公告

今日签到

点亮在社区的每一天
去签到