我正在处理带有泛型的代码,当我使用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())
}