我试图了解泛型如何与 Scala 中的继承一起工作。我有以下代码:
sealed trait Model {}
case class Model1() extends Model
case class Model2() extends Model
trait Repo[A <: Model] {
def doSomething(model: A)
}
class Repo1 extends Repo[Model1] {
override def doSomething(model: Model1): Unit = {
println("Model 1")
}
}
class Repo2 extends Repo[Model2] {
override def doSomething(model: Model2): Unit = {
println("Model 2")
}
}
object Play extends App {
def getModel(i: Int): Model =
i match {
case 1 => Model1()
case 2 => Model2()
case _ => throw new RuntimeException("model not found")
}
val model = getModel(1)
val repo = model match {
case _: Model1 => new Repo1
case _: Model2 => new Repo2
case _ => throw new RuntimeException("something went wrong")
}
repo.doSomething(model)
}
Run Code Online (Sandbox Code Playgroud)
在最后一行repo.doSomething(model)我得到Type mismatch. Required: _.$1 Found: Model
根据这个答案在 Scala 中使用泛型实现 trait 的正确方法是什么?如果我的 repos 类扩展了类型的特征应该可以工作。
我是 Scala 的新手,我正试图围绕类型系统、泛型、隐式、上限/下限...
什么是_.$1类型,我怎样才能使它工作?谢谢!
scala 是静态类型的,并且值model是编译时类型Model和repo编译时类型Repo
所以repo.doSomething没有进一步细化。的签名doSomething表示它将采用Model参数的某个子类型,但我们不知道是哪个子类型——换句话说,编译器不知道align的类型model和类型repo。
要使它们对齐,您有几个选择。
val castRepo = repo.asInstanceOf[Repo[Any]]
Run Code Online (Sandbox Code Playgroud)
这会关闭安全措施,然后您告诉 Scala “相信我,我知道我在做什么”。当您知道自己在做什么时,这在某种程度上很好,但是真正知道自己在做什么的人往往不相信自己比编译器更了解,因此保留类型安全的不同解决方案可能更好。
例如,您可以制作一个包装器类型,就像这样
case class Aligned[A <: Model](model: A, repo: Repo[A]) {
def doIt = repo.doSomething(model)
}
val aligned = model match {
case m: Model1 => Aligned(m, new Repo1)
case m: Model2 => Aligned(m, new Repo2)
case _ => throw new RuntimeException("something went wrong")
}
aligned.doIt
Run Code Online (Sandbox Code Playgroud)
在 中Aligned,scalac 知道Model类型和Repo类型排列。
你甚至不需要实例方法doIt;aligned.repo.doSomething(aligned.model)也有效,因为编译器知道Ainaligned.repo和Ain在对齐中aligned.model都是相同A的。