我试图了解泛型如何在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)
}
在最后一行repo.doSomething(model)
上,我得到Type mismatch. Required: _.$1 Found: Model
根据此答案What is the correct way to implement trait with generics in Scala?,如果我的repos类使用该类型扩展了特征,就应该起作用。
我是Scala的新手,我想把我的头放在类型系统,泛型,隐式,上限/下限...
什么是_.$1
类型,我该如何进行这项工作?谢谢!
答案 0 :(得分:5)
scala是静态类型的,值model
是编译时间类型Model
和repo
的编译时间类型Repo
因此repo.doSomething
没有进一步完善。 doSomething
的签名表示它将使用参数Model
的某些子类型,但是我们不知道哪个子类型,换句话说,编译器不知道{ {1}}和model
的类型对齐。
要使它们对齐,您有几个选择。
repo
关闭安全性,然后您告诉scala“相信我,我知道我在做什么”。当您知道自己在做什么时,这在某种程度上是可以的,但是真正知道自己在做什么的人往往不相信自己比编译器更了解自己,因此保留类型安全性的其他解决方案可能会更好。
例如,您可以创建一个包装器类型
val castRepo = repo.asInstanceOf[Repo[Any]]
在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
中,scalac知道Aligned
类型和Model
类型的排列。
您甚至根本不需要实例方法Repo
; doIt
也可以工作,因为编译器知道aligned.repo.doSomething(aligned.model)
中的A
和aligned.repo
中的A
都是相同的aligned.model
。< / p>