类型系统不允许子类型作为类型构造函数

时间:2019-03-02 21:33:39

标签: scala generics types

我正在尝试为正在编写的库构建以下类型结构,但是在类型系统方面遇到了麻烦。

CarLike

trait CarLike[T, C <: CarLike[T,C]] {
  val parts: Seq[T]
  def crash(other: C, speed: Int): C
  //... other methods
}

SimpleCar

class SimpleCar[T](val parts: Seq[T]) extends CarLike[T, SimpleCar[T]] {
  def crash(other: SimpleCar[T], speed: Int) = {
    //Some logic to crash with the other car
    val newParts = damage(parts) //parts have changed
    new SimpleCar(newParts)
  }
  //...other methods
}

SportsCar

class SportsCar(val sParts: Seq[String]) extends SimpleCar[String](sParts){
  override def crash(other: SimpleCar[String], speed: Int): SimpleCar[String] = {
    //Some other logic for crashing a sport car
    val newParts = damage(parts) //parts have changed
    new SportsCar(newParts)
  }
  //...other methods
}

崩溃者

case class Crasher[T, C <: CarLike[T,C]](
                          partsDamager: T => T,
                          carChecker: C => Seq[T]
                          /*... more parameters*/
                          ){
  def test(cycles:Int) = {
    //Some logic to run a crash of two cars
  }
}

代码

//...
val crasher = Crasher[String, SportsCar](
  (s: String) => s.tail,
  (c: SportsCar) => c.parts.filter(p => p.length > 0)
  /*Many arguments*/
)
crasher.test(20)
//...

该想法是允许库的用户能够在使用默认SimpleCar和实现自己的CarLike实现之间进行选择。 另外,用户可以选择汽车零件的类型。在这个简单的示例中,这些部分是String,但可以轻松地成为自定义类,可以在自定义类的crash方法中加以利用。

编译时,出现以下编译错误:

type arguments [String,my.package.SportsCar] do not conform to method apply's type parameter bounds [T,C <: crashing.CarLike[T,C]]
    val crasher = Crasher[String, SportsCar](

很显然,这里我缺少一些东西。为何编译器不同意SportsCarCarLike的合法子类型?

1 个答案:

答案 0 :(得分:3)

跑车不是CarLike[String, SportsCar],而是CarLike[String, SimpleCar[String]]。请注意,SportsCar extends SimpleCar[String]没什么用,因为CarLikeC中不是协变的。

您无法真正在CarLike中使C协变,因为其crash方法接受C。相反,您可以将SimpleCar[String]传递给Crasher(毕竟,跑车可能会与其他车辆相撞,对吗?):

val crasher = Crasher[String, SimpleCar[String]](
  s => s.tail,
  c => c.parts.filter(p => p.length > 0)
)

或者,您可以修改Crasher以使用另一个类型参数:

case class Crasher[T, C <: CarLike[T, C], X <: C](partsDamager: T => T,
                                                  carChecker: X => Seq[T]) {
  // ...
}

val crasher = Crasher[String, SimpleCar[String], SportsCar](
  s => s.tail,
  c => c.parts.filter(p => p.length > 0)
)