【问题标题】:Scala Type mismatch when a generic type operates on the same generic type当泛型类型对相同的泛型类型进行操作时,Scala 类型不匹配
【发布时间】:2018-06-08 19:16:31
【问题描述】:

我有一个通用案例类 Route,它接受 Location 的子类列表。但是,在以下方法中,我在对 distance expected: head.T, actual: T 的调用中出现类型不匹配的情况@

case class Route[T <: Location](route: List[T]) {
  def measureDistance: Double = {
    def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
      case Nil => acc
      case h :: t => measure(h, t, head.distance(h) + acc)
    }
    if (route.isEmpty) 0.0
    else measure(route.head, route.tail)
  }
}

基本的抽象Location类如下

abstract class Location(val name: String) {

type T <: Location

def distance(that: T): Double
}

由于 head 和 h 都来自同一个列表 route 我不明白为什么它们不是同一类型。

【问题讨论】:

    标签: scala generics types f-bounded-polymorphism


    【解决方案1】:

    在这种情况下,您想要的似乎是 F 有界多态性:

    abstract class Location[L <: Location[L]](val name: String) {
      def distance(that: L): Double
    }
    
    case class Route[T <: Location[T]](route: List[T]) {
      def measureDistance: Double = {
        def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
          case Nil => acc
          case h :: t => measure(h, t, head.distance(h) + acc)
        }
        if (route.isEmpty) 0.0
        else measure(route.head, route.tail)
      }
    }
    

    不过,您也可以考虑改用Metric-typeclass:

    trait Metric[L] {
      def dist(a: L, b: L): Double
    }
    
    case class Route[T: Metric](route: List[T]) {
      def measureDistance: Double = {
        def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
          case Nil => acc
          case h :: t => measure(h, t, implicitly[Metric[T]].dist(head, h) + acc)
        }
        if (route.isEmpty) 0.0
        else measure(route.head, route.tail)
      }
    }
    

    后一种解决方案将适用于更多类型,例如(Double, Double),即使它们不继承自Location。

    这里又是 typeclass 解决方案,但使用了稍微更优美的 Cats 风格的语法,避免了implicitly:

    trait Metric[L] {
      def dist(a: L, b: L): Double
    }
    
    object Metric {
      def apply[T](implicit m: Metric[T]): Metric[T] = m
    }
    
    case class Route[T: Metric](route: List[T]) {
      def measureDistance: Double = {
        def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
          case Nil => acc
          case h :: t => measure(h, t, Metric[T].dist(head, h) + acc)
        }
        if (route.isEmpty) 0.0
        else measure(route.head, route.tail)
      }
    }
    

    【讨论】:

    • 第一种方案解决了类型不匹配的问题。但是,我现在不确定应该如何实现 Location 的子类。我可以像下面这样写它们:case class SubLocation(override val name: String) extends Location(name) { type T = SubLocation override def distance(that: Location[T]): Double = ??? } 但是,一旦接受SubLocation 的其他方法,不再类型检查,用Location[SubLocation] 替换这些方法也不起作用。
    • @jackt 不管你想要什么。例如,如果您的位置是 2d,则定义 class Loc2D(val x: Double, val y: Double, name: String) extends Location[Loc2D](name) { def distance(other: Loc2D): Double = math.hypot(this.x - other.x, this.y - other.y) }。那就是飞机上的欧几里得距离。
    • 啊,我在扩展Location时省略了SubLocation类型。这解决了我的问题。谢谢。
    【解决方案2】:

    您无需在Location 抽象类中定义类型T。您应该按照以下步骤进行:

    abstract class Location[T <: Location[T]](val name: String) {
      def distance(that: T): Double
    }
    
    case class Route[T <: Location[T]](route: List[T]) {
      def measureDistance: Double = {
        def measure(head: T, tail: List[T], acc: Double = 0.0): Double = tail match {
          case Nil => acc
          case h :: t => measure(h, t, head.distance(h) + acc)
        }
        if (route.isEmpty) 0.0
        else measure(route.head, route.tail)
      }
    }
    

    【讨论】:

      【解决方案3】:

      scala 编译器无法知道类Location 中定义的type T &lt;: Location 与Route 的类型参数[T &lt;: Location] 的类型相同。

      我认为您将不得不更改def distance(...) 的签名。我不确定,但如果将 T 定义为 Location 的类型参数,它应该可以工作:

      abstract class Location[T <: Location[T]](val name: String) {
        def distance[T](that: T): Double
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-21
        • 1970-01-01
        • 2015-04-09
        • 1970-01-01
        • 1970-01-01
        • 2013-07-06
        • 1970-01-01
        相关资源
        最近更新 更多