2017-10-20 233 views
2

我试图让一个普通的可怜人的数据持久化功能,将采取数据类MutableSet和它序列化到磁盘。我想要一些易于原型设计的东西,并且可以每隔一段时间在该集合上调用“save()”,以便如果我的进程被终止,我可以稍后使用已保存条目的“load()”继续。科特林“out”和“in”和仿制药 - 正确使用

但是,即使在重读Generics页面之后,我仍然没有明白'*','in','out'和'Nothing'之间的区别。这SEEMS的工作没有抛出错误,但我不明白为什么他们都“出局”,我认为必须是“在”......或者更可能是我对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 

,然后能够揭开序幕任何应用程序与

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

回答

5

您的代码包含一个未投as Collection<Nothing>

进行未经检查的转换是一种告诉编译器,您知道更多关于类型的信息的方法,它允许违反一些限制条件,包括泛型差异引入的限制。

如果删除选中投,只留下了它的检查部分,即

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

编译器不会让你在this.addAll(loaded)行添加的项目。 基本上,你所做的未经检查的演员是一个肮脏的黑客,因为Nothing类型在Kotlin中没有真正的价值,你不应该假装它。它的作用仅仅是因为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>()) 
     } 
    } 
} 

如果要使其与比SerializableAny容纳更多的具体项目组的工作,你可以做它具有通用的类型参数。这使得编译器内联声明/推断类型在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>()) 
     } 
    } 
} 

或检查适合你的另一种方式的项目更好。例如。 addAll行之前的loaded.forEach { if (it !is T) throw IllegalArgumentException() }

+0

第二个选项效果很好,谢谢你解释“Nothing”! –