我有这些模特:
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() }
如果我获得Vehicle[Car]
的实例并调用update()
,我将获得Car
。由于Car
扩展Vehicle[Car]
(或简单地说,Car 是 Vehicle [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[_]
并在元素上调用update()
会产生Any
,但我希望能够使用update()
,因此我必须使用F-bounded类型)。使用存在性类型搞定了类型关系,因为一旦我从Vehicle获取底层汽车/公共汽车,我就不能再把它投射到车辆上了,因为......好吧,它只是一些存在类型:
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
)时,如果是具体的类型,它就可以了 - 例如如果我删除Vehicle[Car]
,我可以安全地声称我删除了Car <: Vehicle[Car]
因为Car
。但如果我撕掉一个存在主义类型,我就无法做任何事情。之前的示例有效,因为Vehicle[Car]
是_
,但在这种情况下,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]
,而不是{{ 1}}
我知道使用无形HList
可以解决问题,因为我不必使用存在类型(我只需要一个汽车和公共汽车列表,并保留类型信息)。但是我想通过一个简单的List
来了解这个特定的用例。
编辑:
@RomKazanova是的,这当然会有效,但我需要在update()
之前和之后保留相同的类型(尽管这里是努力的赞成;))。
我认为没有HList或类似的数据结构是不可能的,因为统一汽车和公共汽车会迫使我们使用车辆类型,这种车型失去了关于其底层类型是汽车,公共汽车还是其他东西的信息(我们都是可以知道它是某种类型_ <: Vehicle
)。但我想和你们一起检查一下。
答案 0 :(得分:4)
我对存在类型不是很好,所以我无法解释太多:-p但是当你将seq
的类型改为List[Vehicle[T] forSome {type T <: Vehicle[T]}]
时到&#34;锻炼&#34;。请注意,您必须将类型传递给List
构造函数/ apply方法。
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
类型构造函数的递归性质。
我不确定我会推荐这个......
答案 1 :(得分: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)