Generics 科特林“;“出去”;及;在;和泛型-正确使用

Generics 科特林“;“出去”;及;在;和泛型-正确使用,generics,kotlin,kotlin-generics,Generics,Kotlin,Kotlin Generics,我试图创建一个通用的穷人数据持久性函数,该函数将获取数据类的可变集,并将其序列化到磁盘。我想要一些易于原型化的东西,并且我可以偶尔在集合上调用“save()”,这样,如果我的进程被终止,我可以稍后继续使用“load()”保存的条目 但是,即使在重新阅读页面之后,我也不太明白“*”、“in”、“out”和“Nothing”之间的区别。这似乎没有抛出错误,但我不明白为什么他们都是“出”,我认为一个将不得不“在”。。。或者更可能的是,我对Kotlin泛型的理解完全错误。有没有正确的方法 /** Sav

我试图创建一个通用的穷人数据持久性函数,该函数将获取数据类的可变集,并将其序列化到磁盘。我想要一些易于原型化的东西,并且我可以偶尔在集合上调用“save()”,这样,如果我的进程被终止,我可以稍后继续使用“load()”保存的条目

但是,即使在重新阅读页面之后,我也不太明白“*”、“in”、“out”和“Nothing”之间的区别。这似乎没有抛出错误,但我不明白为什么他们都是“出”,我认为一个将不得不“在”。。。或者更可能的是,我对Kotlin泛型的理解完全错误。有没有正确的方法

/** 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
/**保存/加载任何可变表集*/
fun MutableSet.save(文件名:String=“persist\u${javaClass.simpleName}.ser”){
val tmpFile=File.createTempFile(文件名“.tmp”)
ObjectOutputStream(gziOutputStream(FileOutputStream(tmpFile)))。使用{
println(“使用${this.size}条目的持久化集合。”)
it.writeObject(此)
}
Files.move(path.get(tmpFile)、path.get(fileName)、StandardCopyOption.REPLACE_EXISTING)
}
load(文件名:String=“persist${javaClass.simpleName}.ser”){
if(文件名).canRead()){
ObjectInputStream(gzip输入流(FileInputStream(文件名)))。使用{
val load=it.readObject()作为集合
println(“正在加载带有${loaded.size}项的集合。”)
this.addAll(已加载)
}
}
} 
数据类mywhere(val源文件:String,val帧:Int):可序列化
然后就可以用

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

您的代码包含未选中的强制转换
作为集合

进行未检查的强制转换是一种告诉编译器您对类型的了解比它多的方法,允许您违反一些限制,包括泛型变量引入的限制

如果删除未选中的强制类型转换并只保留其选中部分,即

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

如果要使其与包含比
Serializable
Any
更多具体项的集合一起工作,可以使用具体化的类型参数。这使编译器在
加载
调用站点内联声明/推断的类型,以便将该类型传播到
过滤器站
,并正确检查项目:

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>())
        }
    }
}
inline fun MutableSet.load(
fileName:String=“persist_${javaClass.simpleName}.ser”
) {
if(文件名).canRead()){
ObjectInputStream(gzip输入流(FileInputStream(文件名)))。使用{
val load=it.readObject()作为集合
println(“正在加载带有${loaded.size}项的集合。”)
this.addAll(已加载.filterIsInstance())
}
}
}

或者用另一种更适合你的方式检查物品。例如,
load.forEach{if(it!is T)在
addAll
行之前抛出illegargumentexception()

第二个选项效果很好,感谢您解释“Nothing”!
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>())
        }
    }
}