gpt4 book ai didi

generics - Kotlin "out"和 "in"和泛型 - 正确使用

转载 作者:行者123 更新时间:2023-12-01 10:26:53 24 4
gpt4 key购买 nike

我试图制作一个通用的穷人数据持久化函数,该函数采用数据类MutableSet 并将其序列化到磁盘。我想要一些易于原型(prototype)制作的东西,并且可以经常在场景上调用“save()”,这样如果我的进程被终止,我可以稍后使用已保存条目的“load()”恢复。

但即使在重读 Generics 之后,我也不太明白“*”、“in”、“out”和“Nothing”之间的区别。页。这 SEEMS 可以在不抛出错误的情况下工作,但我不明白为什么它们都“出”了,我认为一个必须“入”......或者更有可能我对 Kotlin Generics 的理解完全错误。有正确的方法吗?

/** Save/load any MutableSet<Serializable> */
fun MutableSet<out Serializable>.save(fileName:String="persist_${javaClass.simpleName}.ser") {
val tmpFile = File.createTempFile(fileName, ".tmp")
ObjectOutputStream(GZIPOutputStream(FileOutputStream(tmpFile))).use {
println("Persisting collection with ${this.size} entries.")
it.writeObject(this)
}
Files.move(Paths.get(tmpFile), Paths.get(fileName), StandardCopyOption.REPLACE_EXISTING)
}

fun MutableSet<out Serializable>.load(fileName:String="persist_${javaClass.simpleName}.ser") {
if (File(fileName).canRead()) {
ObjectInputStream(GZIPInputStream(FileInputStream(fileName))).use {
val loaded = it.readObject() as Collection<Nothing>
println("Loading collection with ${loaded.size} entries.")
this.addAll(loaded)
}
}
}

data class MyWhatever(val sourceFile: String, val frame: Int) : Serializable

然后能够启动任何应用程序

val mySet = mutableSetOf<MyWhatever>()
mySet.load()

最佳答案

您的代码包含未经检查的转换 as Collection<Nothing> .

进行未经检查的强制转换是一种告诉编译器您比它更了解类型的方式,允许您违反一些限制,包括由泛型方差引入的限制。

如果您删除未选中的类型转换并仅保留其中已选中的部分,即

val loaded = it.readObject() as Collection<*> 

编译器不允许您在 this.addAll(loaded) 中添加项目线。基本上,你所做的未经检查的转换是一个肮脏的黑客,因为 Nothing type 在 Kotlin 中没有真正的值(value),你不应该假装它有。它之所以有效,是因为 MutableSet<out Serializable>同时表示MutableSet<in Nothing> (这意味着实际类型参数被删除——它可以是 Serializable 的任何子类型——并且由于不知道集合的项目类型到底是什么,所以没有什么可以安全地放入集合中)。

实现第二个函数的一种类型安全的方法是:

fun MutableSet<in Serializable>.load(
fileName: String = "persist_${javaClass.simpleName}.ser"
) {
if (File(fileName).canRead()) {
ObjectInputStream(GZIPInputStream(FileInputStream(fileName))).use {
val loaded = it.readObject() as Collection<*>
println("Loading collection with ${loaded.size} entries.")
this.addAll(loaded.filterIsInstance<Serializable>())
}
}
}

如果你想让它与包含比 Serializable 更多具体项目的集合一起工作或 Any ,您可以使用具体化的类型参数来完成。这使得编译器在 load 处内联声明/推断的类型调用站点,以便将类型传播到 filterIsInstance并且正确检查了项目:

inline fun <reified T> MutableSet<in T>.load(
fileName: String = "persist_${javaClass.simpleName}.ser"
) {
if (File(fileName).canRead()) {
ObjectInputStream(GZIPInputStream(FileInputStream(fileName))).use {
val loaded = it.readObject() as Collection<*>
println("Loading collection with ${loaded.size} entries.")
this.addAll(loaded.filterIsInstance<T>())
}
}
}

或者以其他更适合您的方式检查项目。例如。 loaded.forEach { if (it !is T) throw IllegalArgumentException() }addAll 之前行。

关于generics - Kotlin "out"和 "in"和泛型 - 正确使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46854326/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com