Kotlin和泛型混淆
我有一些泛型的Drawers
:
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>
然后我有一个接受这些Drawers
的controller
,把它们放在一个ArrayList
private val drawers = ArrayList<BaseGeoDrawer<Any, Any, Any>>() open fun addGeoDrawer(drawer: BaseGeoDrawer<Any, Any, Any>) { drawers.add(drawer) }
后来在这些Drawers
调用方法
//Method in controller private fun onMarkerClicked(marker: Marker): Boolean { return drawers.any { it.onGeoClicked(marker) } } //Method in BaseGeoDrawer fun onGeoClicked(geo: GEO): Boolean
问题出现在这一行上
teamAreaDrawer = TeamAreaDrawer(this) mapController.addGeoDrawer(teamAreaDrawer)
Android Studio不会允许它,告诉我
Type mismatch. Required: BaseGeoDrawer<Any, Any, Any> Found: TeamAreaDrawer
我试着用drawers
private val drawers = ArrayList<BaseGeoDrawer<out Any, out Any, out Any>>()
但是,然后onMarkerClicked
将不会编译,与以下错误
Out-projected type BaseGeoDrawer<out Any, out Any, out Any> prohibits the use of 'public final fun onGeoClicked(geo: GEO) defined in mypackage.BaseGeoDrawer'
这里的问题是,您需要将GEO
作为BaseGeoDrawer
的逆变类型参数在BaseGeoDrawer
onGeoClicked(GEO)
但是ArrayList<BaseGeoDrawer<Any, Any, Any>>
在其类型中是不变的 。 这意味着您不能添加任何比BaseGeoDrawer<Any, Any, Any>
。 如果您尝试使用BaseGeoDrawer
的类型作为协变 ,它将不会编译,因为当您调用onGeoClicked(GEO)
时,您需要它作为逆变 。
考虑到直到现在在科特林,一个类型参数不能是双变量的 ,唯一的办法就是做一个不受控制的转换。
在这个特定的情况下,你可以这样做:
val teamAreaDrawer = TeamAreaDrawer(this) as BaseGeoDrawer<Any, Any, Any> mapController.addGeoDrawer(teamAreaDrawer)
如果你仔细想想,在Java中,你也应该这样做,因为你会有:
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"); }
我建议你阅读更多关于这里的变化。