当泛型类型对同一泛型类型进行操作时,Scala 类型不匹配
Scala Type mismatch when a generic type operates on the same generic type
我有一个通用案例 class 路线,它接收位置的子 class 列表。但是在下面的方法中,我在 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)
}
}
基本摘要位置class如下
abstract class Location(val name: String) {
type T <: Location
def distance(that: T): Double
}
因为 head 和 h 都来自同一个列表 route
我不明白为什么它们不是同一类型。
在这种情况下,看起来 F-bounded 多态性就是您想要的:
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
类型类:
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
。
这里又是类型类解决方案,但使用了稍微更精致的 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
摘要 class 中定义类型 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)
}
}
scala 编译器无法知道 class Location
中定义的 type T <: Location
与 Route 的类型参数 [T <: Location]
是同一类型。
我认为您将不得不更改 def distance(...)
的签名。我不确定,但如果您将 T 定义为 Location:
的类型参数,它应该可以工作
abstract class Location[T <: Location[T]](val name: String) {
def distance[T](that: T): Double
}
我有一个通用案例 class 路线,它接收位置的子 class 列表。但是在下面的方法中,我在 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)
}
}
基本摘要位置class如下
abstract class Location(val name: String) {
type T <: Location
def distance(that: T): Double
}
因为 head 和 h 都来自同一个列表 route
我不明白为什么它们不是同一类型。
在这种情况下,看起来 F-bounded 多态性就是您想要的:
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
类型类:
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
。
这里又是类型类解决方案,但使用了稍微更精致的 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
摘要 class 中定义类型 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)
}
}
scala 编译器无法知道 class Location
中定义的 type T <: Location
与 Route 的类型参数 [T <: Location]
是同一类型。
我认为您将不得不更改 def distance(...)
的签名。我不确定,但如果您将 T 定义为 Location:
abstract class Location[T <: Location[T]](val name: String) {
def distance[T](that: T): Double
}