What you want to do is slightly tricky. Subtyping, as you've seen, isn't going to get you what you want due to having to return the supertype of all your return types.
Type variables though, might save the day!
def convertType[T](x: String): T = x.asInstanceOf[T]
But that's not quite what you want... You need something that has different behavior based on what T
is being passed. Something like (not real Scala)
def convertType[T](x: String): T = T match {
case Double => x.toDouble
case Int => x.toInt
// And all your other cases
}
Unfortunately Scala is not a full-fledged dependently-typed language so we don't have functions that can act directly on types, so that match statement doesn't work.
However, if we provided a value-level "witness" for the type T
, maybe we could get away with our conversion.
def convertType[T](x: String)(witness: T): T = witness match {
case _: Double => x.toDouble
case _: Int => x.toInt
// Fill in other cases as necessary
}
We can make this slightly more convenient for ourselves by making the witness implicit
. Now that only gets us half-way there because we still need to have weird dummy implicit values sitting around that don't do anything except be witnesses to our function (e.g. randomly we'll need something like implicit val z = 2
).
Can we get rid of the need to create these dummy values to pass in as witnesses?
Yes and there's two ways. One is to use Scala's TypeTags which are exactly meant to be value level witnesses of types generated automatically by the compiler.
import scala.reflect.runtime.universe._
def convertTypes[T](x: String)(implicit witness: TypeTag[T]): T = witness match {
case a if a =:= typeOf[Int] => x.toInt
case a if a =:= typeOf[Double] => x.toDouble
}
Alternatively you can move the actual conversion out to a separate trait and have witnesses implement the behavior specified by the trait.
trait Convertable[T] {
def convertFromString(x: String): T
}
implicit object ConvertInt extends Convertable[Int] {
override def convertFromString(x: String): Int = x.toInt
}
// Do the same for your other types
def convertType[T](x: String)(implicit witness: Convertable[T]): T = witness.convertFromString(x)
And there we go! A nice type-safe-ish conversion method (at least this last one is). You'd probably want to wrap the entire thing in a scala.util.Try
in case you pass a malformed string.
Incidentally we've just recreated typeclasses (or used Scala's built-in TypeTag
typeclass).