2

这是关于表示类型的两个 问题的后续,它们是一个 trait 的类型参数,旨在表示有界类型成员(或类似成员)的基础类型。我已经成功地创建了类的实例,例如ConcreteGarage,具有cars有界类型成员的实例CarType

trait Garage {
  type CarType <: Car[CarType]
  def cars: Seq[CarType]

  def copy(cars: Seq[CarType]): Garage

  def refuel(car: CarType, fuel: CarType#FuelType): Garage = copy(
    cars.map {
      case `car` => car.refuel(fuel)
      case other => other
    })
}

class ConcreteGarage[C <: Car[C]](val cars: Seq[C]) extends Garage {
  type CarType = C
  def copy(cars: Seq[C]) = new ConcreteGarage(cars)
}

trait Car[C <: Car[C]] {
  type FuelType <: Fuel
  def fuel: FuelType

  def copy(fuel: C#FuelType): C

  def refuel(fuel: C#FuelType): C = copy(fuel)
}

class Ferrari(val fuel: Benzin) extends Car[Ferrari] {
  type FuelType = Benzin
  def copy(fuel: Benzin) = new Ferrari(fuel)
}

class Mustang(val fuel: Benzin) extends Car[Mustang] {
  type FuelType = Benzin
  def copy(fuel: Benzin) = new Mustang(fuel)
}

trait Fuel
case class Benzin() extends Fuel

我可以轻松地创建 s 的实例,Car例如Ferraris 和Mustangs 并将它们放入 aConcreteGarage中,只要它很简单:

val newFerrari = new Ferrari(Benzin())
val newMustang = new Mustang(Benzin())

val ferrariGarage = new ConcreteGarage(Seq(newFerrari))
val mustangGarage = new ConcreteGarage(Seq(newMustang))

但是,如果我只是根据标志返回一个或另一个,并尝试将结果放入车库,它会失败:

val likesFord = true
val new_car = if (likesFord) newFerrari else newMustang

val switchedGarage = new ConcreteGarage(Seq(new_car)) // Fails here

单独的开关工作正常,它是对ConcreteGarage构造函数的调用失败并出现相当神秘的错误:

error: inferred type arguments [this.Car[_ >: this.Ferrari with this.Mustang <: this.Car[_ >: this.Ferrari with this.Mustang <: ScalaObject]{def fuel: this.Benzin; type FuelType<: this.Benzin}]{def fuel: this.Benzin; type FuelType<: this.Benzin}] do not conform to class ConcreteGarage's type parameter bounds [C <: this.Car[C]]
val switchedGarage = new ConcreteGarage(Seq(new_car)) // Fails here
                     ^

我曾尝试将那些神奇的[C <: Car[C]]表示类型参数放在任何地方,但没有成功找到神奇的地方。

4

1 回答 1

2

没有任何有用的超类型可以为其Ferrari加上Mustang别名。你需要用这种方法把世界从里到外包裹起来。

一种可能性是将Garage构造作为方法添加到Car.

另一种可能性是定义一些负责生产兼容汽车和车库的“世界”:

trait World {
   type CarType <: Car[CarType]
   def newCar() : CarType
   def newGarage(cars: Seq[CarType]) = new ConcreteGarage[CarType](cars)
}

class FerrariWorld extends World {
   type CarType = Ferrari
   def newCar() = new Ferrari(Benzin())
}

class FordWorld extends World {
   type CarType = Mustang
   def newCar() = new Mustang(Benzin())
}

def play(world: World) {
   val car = world.newCar()
   println(car)
   val gar = world.newGarage(Seq(car))
   println(gar)
}

def test(likesFord: Boolean) {
   val w = if(likesFord) new FordWorld else new FerrariWorld
   play(w)
}

test(true)
test(false)

你可以看到这会变得非常幽闭恐惧症。所以这真的取决于你的目标场景。路径相关类型总是会导致未来的约束。考虑这个带有类型参数的相当简单的变体:

trait Fuel { def liters: Int }
trait Make { def color: String }

case class Benzin(liters: Int = 0) extends Fuel
case class Diesel(liters: Int = 0) extends Fuel
case class Ferrari(color: String) extends Make
case class Mustang(color: String) extends Make { def race() { println( "Rrrroar" )}}

case class Car[M <: Make, F <: Fuel](make: M, fuel: F) {
   def refuel(f: F): Car[M, F] = copy(make, f)
}

case class Garage[M <: Make](cars: Seq[Car[M,_]] = Seq.empty) {
   def add(c: Car[M,_]) = copy(cars :+ c)
   def remove(c: Car[M,_]) = copy(cars.filterNot(_ == c))
   def refuel[F <: Fuel](c: Car[M,F], f: F) = copy( cars.map {
      case `c` => c.refuel(f)
      case other => other
   })
}    

val g0 = Garage[Mustang]()
val m  = Car(Mustang("black"), Benzin())
val f  = Car(Ferrari("red"), Benzin())
val g1 = g0.add(f)                // forbidden
val g1 = g0.add(m)                // ok
val g2 = g1.refuel(f, Benzin(45)) // forbidden
val g2 = g1.refuel(m, Diesel(45)) // forbidden
val g2 = g1.refuel(m, Benzin(45)) // ok
g2.cars.foreach(_.make.race())    // ok

结论:不要走弯路...

在此处输入图像描述

于 2012-06-30T23:25:21.650 回答