5

我有这些模型:

trait Vehicle[T <: Vehicle[T]] { def update(): T }
class Car extends Vehicle[Car] { def update() = new Car() }
class Bus extends Vehicle[Bus] { def update() = new Bus() }

如果我获得 a 的实例Vehicle[Car]并调用update(),我将获得 a Car。由于Carextends Vehicle[Car](或者简单地说,CarVehicle[Car]),我可以安全地将结果的类型显式设置为Vehicle[Car]

val car = new Car
val anotherCar = car.update()
val anotherCarAsVehicle: Vehicle[Car] = car.update() // works as expected

但是,如果我想将Car和的实例Bus放在一个列表中,那么我必须将列表类型设置为Vehicle[_ <: Vehicle[_]](拥有一个简单的列表并在一个元素上Vehicle[_]调用会 yield ,但我希望能够使用所以我必须使用 F 有界类型)。使用存在类型会破坏类型关系,因为一旦我从 Vehicle 中获取底层汽车/公共汽车,我就不能再将其转换为 Vehicle 因为......好吧,它只是一些存在类型:update()Anyupdate()

val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)
val car = seq.head.update()
val carAsVehicle: Vehicle[_ <: Vehicle[_]] = seq.head.update() // fails to compile

因此,Vehicle用某种类型参数化,T该类型是Vehicle[T]. 当我撕掉T(通过使用update())时,在具体类型的情况下没关系 - 例如,如果我撕掉Car,我可以安全地声称我撕掉了 aVehicle[Car]因为Car <: Vehicle[Car]。但是,如果我撕掉一个存在类型,我就无能为力了。前面的示例有效,因为Caris a Vehicle[Car],但在这种情况下_不是 a Vehicle[_]

指定我的具体问题对于上面给出的模型(车辆、汽车、公共汽车),有没有办法实现这一点?

def sameType[T, U](a: T, b: U)(implicit evidence: T =:= U) = true

val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)

sameType(seq.head.update +: seq.tail, seq) // true 

请注意,您可以更改给定的特征、类和类型seq,但有一个限制:update() 必须返回T,而不是Vehicle[T]

我知道使用 shapelessHList可以解决问题,因为我不必使用存在类型(我只需要一个汽车和公共汽车的列表,并且会保留该类型信息)。但我想知道这个特殊的用例是否有一个简单的List.

编辑

@RomKazanova 是的,那当然可以,但是我需要在前后保留相同的类型update()(尽管这是对努力的支持;))。

我相信没有 HList 或类似的数据结构是不可能的,因为统一汽车和公共汽车迫使我们使用车辆类型,这会丢失关于其基础类型是 Car、Bus 还是其他东西的信息(我们所知道的是它是一些类型_ <: Vehicle)。但我想和你们核实一下。

4

2 回答 2

4

我对存在类型不是很好,所以我不能对此解释太多:-p 但是当你将类型更改seqList[Vehicle[T] forSome {type T <: Vehicle[T]}]一切似乎都“解决”了。请注意,您必须将类型传递给List构造函数/应用方法。

scala> val seq = List[Vehicle[T] forSome {type T <: Vehicle[T]}](new Car, new Bus)
seq: List[Vehicle[T] forSome { type T <: Vehicle[T] }] = List(Car@31e53802, Bus@54d569e7)

scala> sameType(seq.head.update +: seq.tail, seq)
res3: Boolean = true

scala> seq.head.update
res4: T forSome { type T <: Vehicle[T] } = Car@79875bd2

scala> seq.head.update.update
res5: T forSome { type T <: Vehicle[T] } = Car@6928c6a0

scala> new Car +: seq
res6: List[Vehicle[T] forSome { type T <: Vehicle[T] }] = List(Car@51f0a09b, Car@31e53802, Bus@54d569e7)

我认为摆脱这个答案的主要问题是,这可以让您阐明Vehicle类型构造函数的递归性质。

我不确定我会推荐这个虽然......

于 2017-02-24T14:10:31.727 回答
2

有两种方法可以解决它:

val carAsVehicle: Vehicle[_] = seq.head.update()

或使用模式匹配

val carAsVehicle: Vehicle[Car] = seq.head match {
  case car: Vehicle[Car] => car.update()
}

但有趣的是:

val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)

val vehicleAsVihecles: List[Vehicle[_]]= seq.map(_.update()) // compiled

val vehicleAsVihecles1: List[Vehicle[_ <: Vehicle[_]]]= seq.map(_.update()) //not compiled

def somedef(vehicles: List[Vehicle[_ <: Vehicle[_]]]) = vehicles.map(_.update()) //compiled
somedef(seq)
于 2017-02-24T11:48:45.417 回答