简体   繁体   English

如何在运行时将Type传递给多态函数scala

[英]How to pass Type at Runtime to Polymorphic function scala

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. 我需要将存储在String中的值转换为Type并将其传递给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 预期的OP:整数时为30,字符串时为1020

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] . 这里最明显的问题是尝试使用l.asInstanceOf[T]String转换为T This is not how things work, you can not just convert a String to a T by doing asInstanceOf . 这不是工作原理,您不能仅通过asInstanceOfString转换为T Let me show when is this asInstanceOf can be used. 让我说明何时可以使用此asInstanceOf

// 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 . 但是仅由于该value已经是一个Int ,所以该variable的类型才是Any

What you are trying to do, it to cast a value of type String to some T . 您要尝试执行的操作是将String类型的value StringT

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. 您应该查看配置库,例如lightbend-config或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. 因此,编译器必须先选择xy的类型,然后才能从配置中读取类型。 Since x and y could one of a number of types, the compiler probably chooses Any . 由于xy可以是多种类型之一,因此编译器可能选择Any

This is theoretically OK so far because an Any variable can hold either Int or String . 到目前为止,这在理论上是可以的,因为Any变量可以容纳IntString But things go wrong when you try to add them: x + y . 但是,当您尝试添加它们时,事情就出错了: x + y This is telling the compiler to add an Any to an Any and this is clearly going to go badly. 这是告诉编译器在Any上添加Any ,这显然很不好。 The program does not use the run-time type of x and y to pick the appropriate + function. 该程序不使用xy的运行时类型来选择适当的+函数。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM