带有 in 的 Kotlin 泛型在编译时会产生类型不匹配



我正在处理带有泛型的代码,当我使用in时,我在编译时遇到了TypeMismatch。

代码如下:

open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected 

我还尝试将属性存储库从 Repo2 更改为 Repo<*>

因为 BRepo 是一个Repo<B>,它不是一个Repo<in A>,(但它会满足Repo<out A>)。

换句话说,Repo<in A>必须能够接受setParam(A()),但BRepo.setParam()只能接受B或B的子类。

或者换句话说,BRepo是一个Repo<B>,在写入值时,它对类型的限制比Repo<A>更严格(但在读取值时限制更宽松)。

class Repo2(val repo: Repo<*>)不起作用的原因是Repo<*>本质上是一个Repo<in Nothing/out A>。您不能在具有任何类型的对象的Repo<*>上调用setParam()

您的代码中存在一个设计缺陷,您无法仅通过更改 Repo2 的构造函数签名来修复该缺陷。就目前而言,Repo2 需要能够将 A 写入您传递给它的对象,而 BRepo 根据定义不支持写入 A,仅支持写入 B。您需要使类的至少一个定义在类型方面更加灵活。

使用更常见的类可能更容易理解协方差限制:

val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5)  // ok
anyList = stringList // Compiler error. 
// You wouldn't be able to call add(5) on an ArrayList<String>

基本上MutableList<String>不是MutableList<in Any>

,就像Repo<B>不是Repo<in A>一样。

Repo2类希望只使用类型A,使用Repo2<T : A>(val repo: Repo<in T>)

open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}

最新更新