Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

201
Vistas
Error de compilación con genéricos de Kotlin e interfaz sellada

Tengo una pregunta sobre Kotlin y los genéricos. Tengo un error de compilación, que no entiendo. Tengo un ejemplo muy simplificado a continuación. Dado que las clases de datos de Kotlin no pueden heredar de otras clases de datos, hice mucho del diseño de mi clase con composición. Eso conduce a clases de datos anidados. En algunos puntos, uso genéricos para cambiar el tipo de parámetros que puedo pasar a un objeto. Necesito modificar los valores de estas clases de datos y debido a que son inmutables, uso la función de copia y coloco esas cosas en una clase Mutator.

 class Mutator<out P : Params>(val form: Form<P>) { fun modifyName(name: String): Form<P> = when (form.params) { is DefaultParams -> form.copy(params = form.params.copy(name = name)) // compile error is ExtendedParams -> form.copy(params = form.params.copy(name = name)) // compile error else -> form // also.. why do I need that.. Params is sealed.. } } sealed interface Params data class DefaultParams(val name: String) : Params data class ExtendedParams(val name: String, val age: Int) : Params data class Form<out P : Params>(val params: P) fun main() { val form: Form<DefaultParams> = Form(DefaultParams("John")) val mutator: Mutator<DefaultParams> = Mutator(form) val newForm: Form<DefaultParams> = mutator.modifyName("Joe") val next_form: Form<ExtendedParams> = Form(ExtendedParams("John", 30)) val next_mutator: Mutator<ExtendedParams> = Mutator(next_form) val next_newForm: Form<ExtendedParams> = next_mutator.modifyName("Joe") }

Recibo errores en las dos primeras ramas del bloque when.

Tipo de discrepancia. Requerido: P Encontrado: DefaultParams

Tipo de discrepancia. Requerido: P Encontrado: ExtendedParams

¿No deberían ser Params el límite superior de P y, por lo tanto, estar bien para encontrar DefaultParams o ExtendedParams ? Y también se deben sellar Params , pero necesito un bloque else en el cuándo.

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Sugeriría que simplemente use un elenco sin marcar aquí:

 is DefaultParams -> form.copy(params = form.params.copy(name = name) as P) is ExtendedParams -> form.copy(params = form.params.copy(name = name) as P)

Tenga en cuenta que esta conversión sin marcar es muy segura, debido a la semántica del método de copy . copy no cambia el tipo de tiempo de ejecución de su receptor. Por lo tanto, no es más peligroso pasar los form.params copiados que pasar los form.params originales. Y el compilador le permite pasar el form.params original.params aquí. El compilador simplemente no es lo suficientemente inteligente como para darse cuenta de que, dado que form.params es un Params específico, P también debe ser ese Params específico.

La instrucción when no es exhaustiva porque el tipo de form.params no es Params , sino P . (Sí, el compilador es bastante estúpido a veces). De acuerdo con spec , una condición para una expresión when exhaustiva es:

La expresión enlazada es de una clase o interfaz sellada

Tenga en cuenta que no dice "un parámetro de tipo limitado a una clase o interfaz sellada". Entonces, si solo agrega as Params , será exhaustivo:

 fun modifyName(name: String): Form<P> = (form.params as Params).let { formParams -> when (formParams) { is DefaultParams -> form.copy(params = formParams.copy(name = name) as P) is ExtendedParams -> form.copy(params = formParams.copy(name = name) as P) } }
over 4 years ago · Santiago Trujillo Denunciar

0

No estoy exactamente seguro de por qué es así, pero creo que tiene algo que ver con que P sea un genérico. Porque si reescribes el Mutador como este puedes omitir el otro y tampoco hay error.

 class Mutator(val form: Form<Params>) { fun modifyName(name: String): Form<Params> = when (form.params) { is DefaultParams -> form.copy(params = form.params.copy(name = name)) // compile error is ExtendedParams -> form.copy(params = form.params.copy(name = name)) // compile error } }

De hecho, creo que para su caso de uso no necesita genéricos y esta solución podría ser exactamente lo que necesita.

EDITAR:

si lo modificas a esto

 class Mutator<out P : Params>(val form: Form<P>) { fun modifyName(name: String): Form<P> = when (form.params) { is DefaultParams -> form.copy(params = (form.params.copy(name = name) as P)) is ExtendedParams -> form.copy(params = (form.params.copy(name = name) as P)) else -> form } }

debería funcionar como querías. Ya no tendrá un error de compilación. Sin embargo, dará una advertencia de que as P es un elenco sin control. Pero para nosotros es fácil ver que esto nunca será un problema. Supongo que el compilador no es lo suficientemente inteligente.

over 4 years ago · Santiago Trujillo Denunciar

0

Debe convertir los parámetros a P porque eso es lo que espera el compilador para la función de copy . Sin embargo, esto le da una advertencia sobre una excepción de conversión no verificada (que es relativamente segura, consulte la respuesta de Sweeper ).

En caso de que desee evitar esta excepción, puede convertir la función modifyName en línea y usar parámetros reificados (consulte los documentos de Kotlin sobre conversiones no verificadas y esta excelente respuesta sobre la palabra clave reificada).

 inline fun<reified P: Params> Form<P>.modifyName(name: String): Form<P> = when (this.params) { is DefaultParams -> copy(params = params.copy(name = name) as P) is ExtendedParams -> copy(params = params.copy(name = name) as P) else -> this } sealed interface Params data class DefaultParams(val name: String) : Params data class ExtendedParams(val name: String, val age: Int) : Params data class Form<out P : Params>(val params: P) fun main() { val form: Form<DefaultParams> = Form(DefaultParams("John")) val newForm: Form<DefaultParams> = form.modifyName("Joe") val nextForm: Form<ExtendedParams> = Form(ExtendedParams("John", 30)) val nextNewForm: Form<ExtendedParams> = nextForm.modifyName("Joe") }

Esto alineará los tipos en el bytecode generado, de modo que cuando llame a form.modifyName("Joe") en el ejemplo anterior, la función modifyName sabrá que P es DefaultParams y, por lo tanto, puede devolver una instancia de Form<DefaultParams> (en lugar de Form<Params> ).

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda