泛型类型如何与 Scala 中的继承一起工作?

Mir*_*c21 0 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)
}
Run Code Online (Sandbox Code Playgroud)

在最后一行repo.doSomething(model)我得到Type mismatch. Required: _.$1 Found: Model

根据这个答案在 Scala 中使用泛型实现 trait 的正确方法是什么?如果我的 repos 类扩展了类型的特征应该可以工作。

我是 Scala 的新手,我正试图围绕类型系统、泛型、隐式、上限/下限...

什么是_.$1类型,我怎样才能使它工作?谢谢!

Mar*_*ijn 5

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

所以repo.doSomething没有进一步细化。的签名doSomething表示它将采用Model参数的某个子类型,但我们不知道是哪个子类型——换句话说,编译器不知道align的类型model和类型repo

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

  1. 因为你知道类型对齐是因为你以一种你比编译器知道更多的方式构造它,所以告诉编译器
val castRepo = repo.asInstanceOf[Repo[Any]]
Run Code Online (Sandbox Code Playgroud)

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

  1. 重组程序,使事情对齐。

例如,您可以制作一个包装器类型,就像这样

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

你甚至不需要实例方法doItaligned.repo.doSomething(aligned.model)也有效,因为编译器知道Ainaligned.repoAin在对齐中aligned.model都是相同A的。

  • @Mirceac21 [此处](https://scastie.scala-lang.org/BalmungSan/0JgoPzOeSeq43UGTpsxZOQ/5) 是按照 Martijn 提出的建议对代码进行的更惯用的重新实现。 (2认同)