通用类型如何在Scala中与继承一起使用?

时间:2020-08-17 14:08:16

标签: scala

我试图了解泛型如何在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类型,我该如何进行这项工作?谢谢!

1 个答案:

答案 0 :(得分:5)

scala是静态类型的,值model是编译时间类型Modelrepo的编译时间类型Repo

因此repo.doSomething没有进一步完善。 doSomething的签名表示它将使用参数Model的某些子类型,但是我们不知道哪个子类型,换句话说,编译器不知道{ {1}}和model的类型对齐。

要使它们对齐,您有几个选择。

  1. 因为您知道类型对齐是因为您以一种比编译器更了解的方式构造了类型,所以请告诉编译器
repo

关闭安全性,然后您告诉scala“相信我,我知道我在做什么”。当您知道自己在做什么时,这在某种程度上是可以的,但是真正知道自己在做什么的人往往不相信自己比编译器更了解自己,因此保留类型安全性的其他解决方案可能会更好。

  1. 重组程序,以便对齐

例如,您可以创建一个包装器类型

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类型的排列。

您甚至根本不需要实例方法RepodoIt也可以工作,因为编译器知道aligned.repo.doSomething(aligned.model)中的Aaligned.repo中的A都是相同的aligned.model。< / p>