Kotlin y la confusión de generics

Tengo algunos Drawers con generics:

 abstract class BaseGeoDrawer<KEY : Any, GEO : Any, ITEM : Any> abstract class BasePolygonDrawer<KEY : Any, ITEM : Any>: BaseGeoDrawer<KEY, Polygon, ITEM> class TeamAreaDrawer : BasePolygonDrawer<String, Team> abstract class BaseMarkerDrawer<KEY : Any, ITEM : Any> : BaseGeoDrawer<KEY, Marker, ITEM> class TeamPositionDrawer : BaseMarkerDrawer<String, Team> 

Luego tengo un controller que acepta estos Drawers , poniéndolos en una ArrayList

 private val drawers = ArrayList<BaseGeoDrawer<Any, Any, Any>>() open fun addGeoDrawer(drawer: BaseGeoDrawer<Any, Any, Any>) { drawers.add(drawer) } 

Y más adelante en los methods de llamada en estos Drawers

 //Method in controller private fun onMarkerClicked(marker: Marker): Boolean { return drawers.any { it.onGeoClicked(marker) } } //Method in BaseGeoDrawer fun onGeoClicked(geo: GEO): Boolean 

El problema aparece en esta línea

 teamAreaDrawer = TeamAreaDrawer(this) mapController.addGeoDrawer(teamAreaDrawer) 

Android Studio no lo permitirá, diciéndome

 Type mismatch. Requinetworking: BaseGeoDrawer<Any, Any, Any> Found: TeamAreaDrawer 

Intenté usar drawers

 private val drawers = ArrayList<BaseGeoDrawer<out Any, out Any, out Any>>() 

Pero entonces onMarkerClicked no comstackrá, con el siguiente error

 Out-projected type BaseGeoDrawer<out Any, out Any, out Any> prohibits the use of 'public final fun onGeoClicked(geo: GEO) defined in mypackage.BaseGeoDrawer' 

    El problema aquí es que necesita GEO como un parámetro de tipo contravariante en BaseGeoDrawer para usar en onGeoClicked(GEO) pero ArrayList<BaseGeoDrawer<Any, Any, Any>> es invariante en su tipo. Esto significa que no puede agregar nada más que un BaseGeoDrawer<Any, Any, Any> . Si intenta utilizar los types de BaseGeoDrawer como covariante , no se comstackrá porque lo necesita como contravariante cuando llama onGeoClicked(GEO) .

    Teniendo en count que hasta ahora en Kotlin un parámetro de tipo no puede ser bivariante , la única forma de hacerlo es realizar un lanzamiento no verificado.

    En este caso específico, puedes hacer:

     val teamAreaDrawer = TeamAreaDrawer(this) as BaseGeoDrawer<Any, Any, Any> mapController.addGeoDrawer(teamAreaDrawer) 

    Si lo piensas bien, en Java, hubieras hecho lo mismo, porque hubieras tenido:

     List<BaseGeoDrawer> drawers = new ArrayList<>(); public void example() { TeamAreaDrawer teamAreaDrawer = new TeamAreaDrawer(); drawers.add(teamAreaDrawer); // This is an unchecked cast. drawers.get(0).onGeoClicked("Example"); } 

    Te recomiendo que leas más sobre la varianza aquí .