Below is a sample example. Where i need to convert value stored in String as a Type and pass that to polymorphic function in scala.
import scala.reflect.runtime.universe._
import scala.reflect.api
object Test {
def convert[T](l: String)(implicit typeTag: TypeTag[T]): T = l.asInstanceOf[T]
implicit def stringToTypeTag[A](name: String): TypeTag[A] = {
val c = Class.forName(name)
val mirror = runtimeMirror(c.getClassLoader)
val sym = mirror.staticClass(name)
val tpe = sym.selfType
TypeTag(mirror, new api.TypeCreator {
def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) =
if (m eq mirror) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
})
}
def main(args:Array[String]): Unit = {
val typ = "Integer"
val x = convert("10")(stringToTypeTag("java.lang." + typ))
val y = convert("20")(stringToTypeTag("java.lang." + typ))
println(x.getClass)
println(y.getClass)
val z = x + y
println(z)
// Expected OP 30
val typ = "String"
val x1 = convert("10")(stringToTypeTag("java.lang." + typ))
val y1 = convert("20")(stringToTypeTag("java.lang." + typ))
println(x1.getClass)
println(y1.getClass)
val z1 = x1 + y1
println(z1)
// Expected OP 1020
}
}
Expected OP: 30 when Integer and 1020 when String
There are too many problems in this code, I am stating just the most obvious one as the deeper problems require a blog and not an answer.
So, this method,
def convert[T](l: String)(implicit typeTag: TypeTag[T]): T = l.asInstanceOf[T]
The most obvious problem here is trying to convert a String
to T
by using l.asInstanceOf[T]
. This is not how things work, you can not just convert a String
to a T
by doing asInstanceOf
. Let me show when is this asInstanceOf
can be used.
// Lets say, there was an Int
scala> val i: Int = 10
// i: Int = 10
// but it was somehow assigned to a variable of type Any
scala> val a: Any = i
// a: Any = 10
// Now, even if we know that the value is an Int
// but since the variable is Any, we can not do Int like things on it
scala> a / 2
// <console>:13: error: value / is not a member of Any .
// a / 2
// ^
// you can use `asInstanceOf` again access it as an Int
scala> a.asInstanceOf[Int] / 2
// res4: Int = 5
But only because the value
was already an Int
, just the variable
was of type Any
.
What you are trying to do, it to cast a value
of type String
to some T
.
Other than this you are mixing a lot of compile time things with run time things, which will not work out for a lot of reasons.
You should look into a config library such as lightbend-config or pureconfig. Or a reader implementation such as following.
trait ConfigReader[A] {
def read(input: String): A
}
object ConfigReader {
object Implicits {
implicit val intConfigReader = new ConfigReader[Int] {
override def read(input: String): Int = input.toInt
}
implicit val doubleConfigReader = new ConfigReader[Double] {
override def read(input: String): Double = input.toDouble
}
implicit val stringConfigReader = new ConfigReader[String] {
override def read(input: String): String = input
}
}
def read[A](input: String)(implicit configReader: ConfigReader[A]) = configReader.read(input)
}
import ConfigReader.Implicits._
val i = ConfigReader.read[Int]("5")
val d = ConfigReader.read[Double]("5.0")
This is not going to work because function polymorphism is done at compile time, not run time. So you can't select a polymorphic function based on the name of a type that is read from a file.
The underlying problem is that the type of a variable is determined at compile time. So the compiler has to pick the type of x
and y
before the type is read from the configuration. Since x
and y
could one of a number of types, the compiler probably chooses Any
.
This is theoretically OK so far because an Any
variable can hold either Int
or String
. But things go wrong when you try to add them: x + y
. This is telling the compiler to add an Any
to an Any
and this is clearly going to go badly. The program does not use the run-time type of x
and y
to pick the appropriate +
function.
Considering the new requirement details given in the comments, you can do something like following.
import scala.reflect.ClassTag
import scala.util.{Try, Success, Failure}
case class Repr[+A: ClassTag](value: A)
def readInputAsRepr(
input: String,
valueType: String
): Try[Repr[Any]] =
valueType match {
case "Int" => Try(Repr[Integer](input.toInt))
case "Double" => Try(Repr[Double](input.toDouble))
case "String" => Try(Repr[String](input))
case _ => Failure(new UnsupportedOperationException)
}
def intSumAction(i1: Int, i2: Int) = i1 + i2
def doubleSumAction(d1: Double, d2: Double) = d1 + d2
def stringSumAction(s1: String, s2: String) = s1 + s2
def selectAndEvaluateAction(
repr1: Repr[Any],
repr2: Repr[Any]
): Try[Repr[Any]] =
(repr1.value, repr2.value) match {
case (a1: Int, a2: Int) => Success(Repr(intSumAction(a1, a2)))
case (a1: Double, a2: Double) => Success(Repr(doubleSumAction(a1, a2)))
case (a1: String, a2: String) => Success(Repr(stringSumAction(a1, a2)))
case _ => Failure(new UnsupportedOperationException)
}
val x1 = readInputAsRepr("10", "Int").get
val y1 = readInputAsRepr("20", "Int").get
val z1 = selectAndEvaluateAction(x1, y1)
val x2 = readInputAsRepr("10", "String").get
val y2 = readInputAsRepr("20", "String").get
val z2 = selectAndEvaluateAction(x2, y2)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.