smeeb
smeeb

Reputation: 29477

Returning Options of Class types in Scala methods

I am struggling with handling Class types for something I am writing, and the answer may involve generics, but I'm kind of stuck on what the right solution should be.

I have a Scala class Vehicle that has several child classes (Car, Bus, Train, Motorcycle, etc.):

class Vehicle {
    ...
}

class Car extends Vehicle {
    ...
}

class Motorcycle extends Vehicle {
    ...
}

I now am trying to write a utility method that can map a String to a Vehicle subclass (that is, the Class it is, not an instance of that class):

object VehicleUtils {
    def mapToVehicleType(vehicleType : String) : Vehicle = {
        var vType : Vehicle = null
        if(vehicleType == "car") {
            vType = Car
        } else if(vehicleType == "motorcycle") {
            vtype = Motorcycle
        } else if(...) {
            ...etc.
        }

        vType
    }
}

The problem is I'm confusing/blurring types vs. instances. I don't want the mapToVehicleType method to return an instance of, say, Car. I want it to simply return the Car class if "car" is provided as input, etc. Can anyone spot where I'm going awry and what the solution is. I'd also like to use Scala Options if possible, because the mapToVehicleType method can certainly return null.

Upvotes: 1

Views: 4318

Answers (2)

john_omalley
john_omalley

Reputation: 1398

Why do you want the class? That's really the key question. If you're going to dynamically instantiate using reflection you might consider a different pattern.

Reflection is one of those things that is often used to overcome the limits of Java (I'm going to assume that you are coming from Java). But there's usually a better pattern in Scala.

You can return the class (e.g. classOf[Car]) but consider a better algorithm.

Let's say, for the sake of argument, you're returning the class as a "factory". I'm not sure that's the best pattern but instead of reflection you might consider something like this:

object GetVehicleCreator {
  def apply(vehicleType: String): Option[() => Vehicle] = vehicleType match {
    case "car" =>
      Some(() => new Car)
    case "motorcycle" =>
      Some(() => new Motorcycle)
    case _ =>
      None
  }
}

Notice that it's not a "utils" object. The use of "utils" is an anti-pattern in Scala IMO (honestly I don't like it that much in Java either). Just create a function. Anything with an apply method is a function. To use:

val vehicleOption: Option[Vehicle] = for {
  fn <- GetVehicleFactory("car")
  vehicle <- fn()
} yield vehicle

... but again I'd have to know what you're trying to accomplish. I'd bet there's a better pattern for it.

Upvotes: 1

Dia Kharrat
Dia Kharrat

Reputation: 6006

Scala provides classOf[T] to get the class type of a class. For example: classOf[Car] returns an instance of type Class[Car].

For mapping to the appropriate class type, you can use pattern matching to map the types from the provided string to an Option:

def mapToVehicleType(vehicleType: String): Option[Class[_ <: Vehicle]] = {
    vehicleType match {
        case "car"        => Some(classOf[Car])
        case "motorcycle" => Some(classOf[Motorcycle])
        case _            => None
    }
}

Upvotes: 3

Related Questions