如何在Scala中创建带有构造函数参数且没有参数的Case类实例?

如何在Scala中创建带有构造函数参数且没有参数的Case类实例?

问题描述:

我正在制作一个由反射字段值设置的Scala应用程序.这样就可以了.

I'm making a Scala app that sets by reflection field values. This works OK.

但是,为了设置字段值,我需要创建一个实例.如果我有一个带有空构造函数的类,则可以使用classOf [Person] .getConstructors ....

However, in order to set field values I need a created instance. If I have a class with an empty constructor, I can do this easily with classOf[Person].getConstructors....

但是,当我尝试使用具有非空构造函数的Case类来执行此操作时,它将无法正常工作.我具有所有字段名称及其值以及需要创建的对象类型.我能以某种方式实例化案例类吗?

However, when I try doing this with a Case class with a non empty constructor It doesn't work. I have all of the field names and its values, as well as the Object type I need to create. Can I instance the Case Class somehow with what I've got?

我唯一没有的是Case Class构造函数的参数名称,或者是一种不用参数创建此参数然后通过反射设置值的方法.

The only thing I don't have is the parameter names from the Case Class constructor or a way to create this without parameter and then setting the values via reflection.

我们来看这个例子.

我有以下内容

case class Person(name : String, age : Int)
class Dog(name : String) {
    def this() = {
        name = "Tony"
    }
}

class Reflector[O](obj : O) {

    def setValue[F](propName : String, value : F) = ...

    def getValue(propName : String) = ...
}

//This works
val dog = classOf[Dog].newInstance()
new Reflector(dog).setValue("name", "Doggy")

//This doesn't
val person = classOf[Person].newInstance //Doesn't work

val ctor = classOf[Person].getConstructors()(0)
val ctor.newInstance(parameters) //I have the property names and values, but I don't know 
// which of them is for each parameter, nor I name the name of the constructor parameters

case类应具有默认args,以便您可以Person();在没有默认参数的情况下,为名称提供null可能(或应该)达到require(name!= null).

The case class should have default args, so that you can just Person(); in the absence of a default arg, supplying a null for name might (or ought to) hit a require(name != null).

或者,使用反射来找出哪些参数具有默认值,然后为其余参数提供null或0.

Alternatively, use reflection to figure out which params have defaults and then supply nulls or zeros for the rest.

import reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._

// case class instance with default args

// Persons entering this site must be 18 or older, so assume that
case class Person(name: String, age: Int = 18) {
  require(age >= 18)
}

object Test extends App {

  // Person may have some default args, or not.
  // normally, must Person(name = "Guy")
  // we will Person(null, 18)
  def newCase[A]()(implicit t: ClassTag[A]): A = {
    val claas = cm classSymbol t.runtimeClass
    val modul = claas.companionSymbol.asModule
    val im = cm reflect (cm reflectModule modul).instance
    defaut[A](im, "apply")
  }

  def defaut[A](im: InstanceMirror, name: String): A = {
    val at = newTermName(name)
    val ts = im.symbol.typeSignature
    val method = (ts member at).asMethod

    // either defarg or default val for type of p
    def valueFor(p: Symbol, i: Int): Any = {
      val defarg = ts member newTermName(s"$name$$default$$${i+1}")
      if (defarg != NoSymbol) {
        println(s"default $defarg")
        (im reflectMethod defarg.asMethod)()
      } else {
        println(s"def val for $p")
        p.typeSignature match {
          case t if t =:= typeOf[String] => null
          case t if t =:= typeOf[Int]    => 0
          case x                        => throw new IllegalArgumentException(x.toString)
        }
      }
    }
    val args = (for (ps <- method.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
    (im reflectMethod method)(args: _*).asInstanceOf[A]
  }

  assert(Person(name = null) == newCase[Person]())
}