斯卡拉 2.10 : Dynamically instantiating case classes

标签 scala reflection types

我有一个“动态实例化案例类”的梦想——并根据每个字段类型为字段提供一些虚拟数据(我稍后会为此创建一些规则)

到目前为止,我有一些代码可以与 String 的案例类一起使用; LongInt ... 如果有可能处理嵌入式案例类,我有点卡住了

所以我可以实例化case class RequiredAPIResponse (stringValue: String, longValue: Long, intVlaue: Int)

但不是外部; Outer 在哪里...

case class Inner (deep: String)
case class Outer (in : Inner)

代码是

 def fill[T <: Object]()(implicit mf: ClassTag[T]) : T = {

      val declaredConstructors = mf.runtimeClass.getDeclaredConstructors
      if (declaredConstructors.length != 1)
      Logger.error(/*T.toString + */" has " + declaredConstructors.length + " constructors --- only 1 currently supported.")
      val constructor = declaredConstructors.headOption.get

      val m = constructor.getParameterTypes.map(p => {
          Logger.info("getName " + p.getName +" --- getCanonicalName " + p.getCanonicalName)
          Logger.info(p.getCanonicalName)

        p.getCanonicalName match {
          case "java.lang.String" => /*"Name"->*/ val s : java.lang.String = "DEFAULT STRING"
            s
          case "long" => /*"Name"-> */ val l : java.lang.Long = new java.lang.Long(99)
            l
          case "int" => /*"Name"->*/ val i : java.lang.Integer = new java.lang.Integer(99)
            i
          case _ => /*"Name"->*/

            So around here I am stuck!
        //THIS IS MADE UP :) But I want to get the "Type" and recursively call fill     
            //fill[p # Type] <- not real scala code

            //I can get it to work in a hard coded manner
            //fill[Inner]

        }
      })

我感觉像是Scala: How to invoke method with type parameter and manifest without knowing the type at compile time?上的最后一个答案是答案的起点。 所以不要使用 T <: Object; fill 应该采用 ClassTag 还是 TypeTag?

此代码从 - How can I transform a Map to a case class in Scala? 开始- 其中提到(正如 Lift-Framework 所做的那样)我确实有 liftweb 源代码;但迄今为止未能解开其所有 secret 。

编辑 --- 根据 Imm 的观点,我可以使用以下代码(对他的回答进行一些小更新)

def fillInner(cls: Class[_]) : Object = {
    val declaredConstructors = cls.getDeclaredConstructors
    if (declaredConstructors.length != 1)
      Logger.error(/*T.toString + */ " has " + declaredConstructors.length + " constructors --- only 1 currently supported.")
    val constructor = declaredConstructors.headOption.get

    val m = constructor.getParameterTypes.map(p => {
      Logger.info("getName " + p.getName + " --- getCanonicalName " + p.getCanonicalName)
      Logger.info(p.getCanonicalName)

      p.getCanonicalName match {
        case "java.lang.String" => /*"Name"->*/ val s: java.lang.String = "DEFAULT STRING"
          s
        case "long" => /*"Name"-> */ val l: java.lang.Long = new java.lang.Long(99)
          l
        case "int" => /*"Name"->*/ val i: java.lang.Integer = new java.lang.Integer(99)
          i
        case _ => fillInner(p)
      }

    })

    constructor.newInstance(m: _*).asInstanceOf[Object]

  }

    def fill[T](implicit mf: ClassTag[T]) : T = fillInner(mf.runtimeClass).asInstanceOf[T]

谢谢, 布伦特

最佳答案

你实际上并没有使用 ClassTag,只是 Class[_],而且这些都不是类型安全的(它只是 Java 反射),所以只需传递Class[_] 递归:

def fillInner(cls: Class[_]) : Any = {
  val declaredConstructors = cls.getDeclaredConstructors
  if (declaredConstructors.length != 1)
  Logger.error(/*T.toString + */" has " + declaredConstructors.length + " constructors --- only 1 currently supported.")
  val constructor = declaredConstructors.headOption.get

  val m = constructor.getParameterTypes.map(p => {
      Logger.info("getName " + p.getName +" --- getCanonicalName " + p.getCanonicalName)
      Logger.info(p.getCanonicalName)

    p.getCanonicalName match {
      case "java.lang.String" => /*"Name"->*/ val s : java.lang.String = "DEFAULT STRING"
        s
      case "long" => /*"Name"-> */ val l : java.lang.Long = new java.lang.Long(99)
        l
      case "int" => /*"Name"->*/ val i : java.lang.Integer = new java.lang.Integer(99)
        i
      case _ => fillInner(p)
    }
  })

def fill[T: ClassTag]: T = fillInner(classOf[T].runtimeClass).asInstanceOf[T]

但是您可能可以通过使用 Shapeless 以类型安全的方式完成您想做的事情:

trait Supplier[T] {
  def supply: T
}
object Supplier[T] {
  implicit val intSupplier = new Supplier[Int] {
    def supply = 99
  }
  implicit val stringSupplier = ...
  implicit val emptyHListSupplier = new Supplier[HNil] {
    def supply = HNil
  }
  implicit def consHListSupplier[H, T <: HList](
    implicit headSupplier: Supplier[H], 
      tailSupplier: Supplier[T]) = new Supplier[H :: T] {
    def supply = headSupplier.supply :: tailSupplier.supply
   }
}

然后通过隐式解析的魔力,您可以获得 Supplier[(String::HNil)::Int::HNil] 或任何递归 HList HList 最终只包含您拥有 Supplier 的值;你只需要更无形的东西(在版本 1 或 2 中有所不同,我已经有一段时间没做过了,所以我不记得具体细节了)在这些和案例类之间来回转换。

关于斯卡拉 2.10 : Dynamically instantiating case classes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27823448/

相关文章:

scala - 提供的依赖项未能提供

Java - 检查方法是否存在而不是 NoSuchMethodException

c# - 反射.Net : how to load dependencies?

c# - 如何使用属性将方法作为表达式传递?

c# - C# 中的 java.lang.Long

json - 使用 json4s 在 Scala 应用程序中生成 json

Scala 嵌套循环产量

java - 如何安全地更改变量 intellij 的类型?

mysql - 在 MySQL 中,十进制和 double 之间有什么区别(性能方面)吗?

scala - 如何转换 Spark 数据框以使我的值成为列名称?