【问题标题】:How do generic types work with inheritance in scala?泛型类型如何与scala中的继承一起使用?
【发布时间】:2020-08-18 15:08:49
【问题描述】:

我试图了解泛型如何在 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 类型,我怎样才能使它工作?谢谢!

【问题讨论】:

  • 请注意,您可以通过丢弃 Play 中引用 Model2Repo2 的代码来使一切正常,这可能表明该示例被过度最小化了。
  • 你说得对,我把事情简化了。其中一些类存在于不同的包中。感谢您的来信!

标签: scala


【解决方案1】:

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

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

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

  1. 因为您知道类型对齐是因为您以比编译器了解更多的方式构造它,所以请告诉编译器
val castRepo = repo.asInstanceOf[Repo[Any]]

这会关闭安全装置,然后你告诉 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

Aligned 内,scalac 知道Model 类型和Repo 类型排列。

你甚至不需要实例方法doItaligned.repo.doSomething(aligned.model) 也可以工作,因为编译器知道aligned.repo 中的Aaligned.model 中的A 在对齐时都是相同的A

【讨论】:

  • 我怀疑编译器很困惑,但我希望有一种方法/模式可以让它工作。我认为您的第二个解决方案非常优雅,我将使用它。感谢您的回答!
  • @Mirceac21 here 是按照 Martijn 的建议对您的代码进行了更惯用的重新实现。
  • 感谢@LuisMiguelMejíaSuárez 提供的示例。我的代码被简化以突出问题并使问题通用。您的示例对于遇到类似问题的任何人都是一个很好的参考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多