err84
err84

Reputation: 7

How to generalize scala type that works in Array

I have below code:

trait Vehicle {
  def regNumber: String
  def lotSize: Int
}
case class Car(regNumber: String) extends Vehicle {override val lotSize = 2}
case class Motorcycle(regNumber: String) extends Vehicle {override val lotSize = 1}

trait AbstractLot[+T <: Vehicle] {
  def vehicle: T
  def lotSize: Int = vehicle.lotSize
}
final case class CarLot(vehicle: Car) extends AbstractLot[Car]
final case class MotorcycleLot(vehicle: Motorcycle) extends AbstractLot[Motorcycle]

I want to have a ParkingLot type which is an array of option of abstract lot. But, Scala compiler is not happy when I wrote below code

object ParkingLot {
  type ParkingLot = Array[Option[AbstractLot[_]]]

  //Case 1: OK
  def mixMotorcycleCar(motorcycleCapacity: Int, carCapacity: Int): ParkingLot =
    Array.fill[Option[MotorcycleLot]](motorcycleCapacity)(None) +: Array.fill[Option[CarLot]](carCapacity)(None)

  //Case 2: Not OK
  //T is a subclass of AbstractLot. Why isn't this working?
  def homogenous[T <: AbstractLot[_]](capacity: Int): ParkingLot = Array.fill[Option[T]](capacity)(None)

  //Case 3: Not OK
  def carOnly(capacity: Int): ParkingLot = Array.fill[Option[CarLot]](capacity)(None)

}

What did I do wrong? And how should I fix my code so that I can have a ParkingLot type that works in Case 2 and Case 3?

Upvotes: 0

Views: 72

Answers (1)

Ava
Ava

Reputation: 838

The problem here is that mutable collections have invariant element types in Scala so you can't store Option[CarLot] nor Option[MotorcycleLot] elements in Array[Option[AbstractLot[Any]]]. The reason for this can be found here: relation between variance and mutabilty / immutability in Scala.

ParkingLot object is used mainly for space preallocation which is rarely needed in Scala. You have to rethink your design because it won't be possible to create any mutable collection with covariant type. The simplest way to represent a parking lot using a mutable collection would be something like this:

val parkingLot = ArrayBuffer[Vehicle]()
parkingLot.addOne(Car("123"))
parkingLot.addOne(Motorcycle("321"))

Upvotes: 1

Related Questions