Kotlin "fuera" y "en" y generics – uso correcto

Intentaba crear una function de persistencia de datos genérica de un pobre que tomara un MutableSet de class de datos y lo serializara en el disco. Me gustaría algo fácil para la creación de prototypes, y estoy bien llamar "save ()" en el set de vez en cuando para que si mi process muere, puedo reanudar con una "carga ()" de las inputs guardadas.

Pero aún no entiendo las diferencias entre '*', 'in', 'out' y 'Nothing' incluso después de volver a leer la página de Generics . Esto PARECE funcionar sin tirar errores, pero no entiendo por qué con ambos estando "fuera", pensé que uno tendría que estar "adentro" … o más probablemente estoy entendiendo los Genéricos de Kotlin completamente equivocados. ¿Hay una forma correcta de hacer esto?

/** 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 

y luego poder patear cualquier aplicación con

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

    Su código contiene un elenco sin marcar as Collection<Nothing> .

    Hacer un lanzamiento no verificado es una forma de decirle al comstackdor que usted sabe más sobre los types que lo que hace, lo que le permite violar algunas restricciones, incluidas las introducidas por la varianza de los generics.

    Si elimina el yeso no seleccionado y deja solo la parte marcada, es decir,

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

    El comstackdor no le permitirá agregar los elementos en la línea this.addAll(loaded) . Básicamente, el elenco sin marcar que hiciste es un hack sucio, porque el tipo Nothing no tiene valores reales en Kotlin, y no debes pretender que lo haga. Funciona solo porque MutableSet<out Serializable> al mismo time significa MutableSet<in Nothing> (lo que significa que el argumento de tipo real se borra – puede ser cualquier subtipo de Serializable – y como no se sabe cuál es exactamente el tipo de elementos) del set, no hay nada que puedas poner de forma segura en el set).

    Una de las forms seguras de tipo para implementar la segunda function es:

     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>()) } } } 

    Si desea que funcione con sets que contienen más elementos concretos que Serializable o Any , puede hacerlo con parameters de tipo reificado. Esto hace que el comstackdor incorpore el tipo declarado / inferido en los sitios de llamadas de load , de modo que el tipo se propague a filterIsInstance y los elementos se verifiquen correctamente:

     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>()) } } } 

    O verifique los artículos de otra manera que le quede mejor. Por ejemplo, loaded.forEach { if (it !is T) throw IllegalArgumentException() } antes de addAll line.