Scala 2.10:动态实例化案例类
Scala 2.10: Dynamically instantiating case classes
我有一个梦想 "dynamically instantiating case classes" -- 并根据每个字段类型为字段提供一些虚拟数据(我稍后会为此创建一些规则)
到目前为止,我有一些代码可以与 String
的案例类一起使用; Long
或 Int
... 如果可以处理嵌入式案例类,我会有点卡住
所以我可以实例化case class RequiredAPIResponse (stringValue: String, longValue: Long, intVlaue: Int)
但不是外层;其中外层是...
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 源代码;但迄今为止未能解开它的所有秘密。
编辑 --- 根据 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[_]
,并且 none 是类型安全的(它只是 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 不同,我已经有一段时间没做过了,所以我不记得具体细节了)在它们和大小写之间来回转换 类 .
如果只在测试中使用,最好的方法是使用Scala/Java反射。
与使用宏相比的优势在于编译速度更快。与使用 scalacheck 相关的库相比,优势在于 API 更好。
设置它有点复杂。这是一个完整的工作代码,您可以将其复制到您的代码库中:
import scala.reflect.api
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.runtime.universe._
object Maker {
val mirror = runtimeMirror(getClass.getClassLoader)
var makerRunNumber = 1
def apply[T: TypeTag]: T = {
val method = typeOf[T].companion.decl(TermName("apply")).asMethod
val params = method.paramLists.head
val args = params.map { param =>
makerRunNumber += 1
param.info match {
case t if t <:< typeOf[Enumeration#Value] => chooseEnumValue(convert(t).asInstanceOf[TypeTag[_ <: Enumeration]])
case t if t =:= typeOf[Int] => makerRunNumber
case t if t =:= typeOf[Long] => makerRunNumber
case t if t =:= typeOf[Date] => new Date(Time.now.inMillis)
case t if t <:< typeOf[Option[_]] => None
case t if t =:= typeOf[String] && param.name.decodedName.toString.toLowerCase.contains("email") => s"random-$arbitrary@give.asia"
case t if t =:= typeOf[String] => s"arbitrary-$makerRunNumber"
case t if t =:= typeOf[Boolean] => false
case t if t <:< typeOf[Seq[_]] => List.empty
case t if t <:< typeOf[Map[_, _]] => Map.empty
// Add more special cases here.
case t if isCaseClass(t) => apply(convert(t))
case t => throw new Exception(s"Maker doesn't support generating $t")
}
}
val obj = mirror.reflectModule(typeOf[T].typeSymbol.companion.asModule).instance
mirror.reflect(obj).reflectMethod(method)(args:_*).asInstanceOf[T]
}
def chooseEnumValue[E <: Enumeration: TypeTag]: E#Value = {
val parentType = typeOf[E].asInstanceOf[TypeRef].pre
val valuesMethod = parentType.baseType(typeOf[Enumeration].typeSymbol).decl(TermName("values")).asMethod
val obj = mirror.reflectModule(parentType.termSymbol.asModule).instance
mirror.reflect(obj).reflectMethod(valuesMethod)().asInstanceOf[E#ValueSet].head
}
def convert(tpe: Type): TypeTag[_] = {
TypeTag.apply(
runtimeMirror(getClass.getClassLoader),
new TypeCreator {
override def apply[U <: Universe with Singleton](m: api.Mirror[U]) = {
tpe.asInstanceOf[U # Type]
}
}
)
}
def isCaseClass(t: Type) = {
t.companion.decls.exists(_.name.decodedName.toString == "apply") &&
t.decls.exists(_.name.decodedName.toString == "copy")
}
}
而且,当你想使用它时,你可以调用:
val user = Maker[User]
val user2 = Maker[User].copy(email = "someemail@email.com")
以上代码生成任意且唯一的值。它们并不是完全随机的。 API 非常好。考虑到它使用反射,最适合在测试中使用。
在此处阅读我们的完整博客 post:https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html
我有一个梦想 "dynamically instantiating case classes" -- 并根据每个字段类型为字段提供一些虚拟数据(我稍后会为此创建一些规则)
到目前为止,我有一些代码可以与 String
的案例类一起使用; Long
或 Int
... 如果可以处理嵌入式案例类,我会有点卡住
所以我可以实例化case class RequiredAPIResponse (stringValue: String, longValue: Long, intVlaue: Int)
但不是外层;其中外层是...
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 源代码;但迄今为止未能解开它的所有秘密。
编辑 --- 根据 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[_]
,并且 none 是类型安全的(它只是 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 不同,我已经有一段时间没做过了,所以我不记得具体细节了)在它们和大小写之间来回转换 类 .
如果只在测试中使用,最好的方法是使用Scala/Java反射。
与使用宏相比的优势在于编译速度更快。与使用 scalacheck 相关的库相比,优势在于 API 更好。
设置它有点复杂。这是一个完整的工作代码,您可以将其复制到您的代码库中:
import scala.reflect.api
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.runtime.universe._
object Maker {
val mirror = runtimeMirror(getClass.getClassLoader)
var makerRunNumber = 1
def apply[T: TypeTag]: T = {
val method = typeOf[T].companion.decl(TermName("apply")).asMethod
val params = method.paramLists.head
val args = params.map { param =>
makerRunNumber += 1
param.info match {
case t if t <:< typeOf[Enumeration#Value] => chooseEnumValue(convert(t).asInstanceOf[TypeTag[_ <: Enumeration]])
case t if t =:= typeOf[Int] => makerRunNumber
case t if t =:= typeOf[Long] => makerRunNumber
case t if t =:= typeOf[Date] => new Date(Time.now.inMillis)
case t if t <:< typeOf[Option[_]] => None
case t if t =:= typeOf[String] && param.name.decodedName.toString.toLowerCase.contains("email") => s"random-$arbitrary@give.asia"
case t if t =:= typeOf[String] => s"arbitrary-$makerRunNumber"
case t if t =:= typeOf[Boolean] => false
case t if t <:< typeOf[Seq[_]] => List.empty
case t if t <:< typeOf[Map[_, _]] => Map.empty
// Add more special cases here.
case t if isCaseClass(t) => apply(convert(t))
case t => throw new Exception(s"Maker doesn't support generating $t")
}
}
val obj = mirror.reflectModule(typeOf[T].typeSymbol.companion.asModule).instance
mirror.reflect(obj).reflectMethod(method)(args:_*).asInstanceOf[T]
}
def chooseEnumValue[E <: Enumeration: TypeTag]: E#Value = {
val parentType = typeOf[E].asInstanceOf[TypeRef].pre
val valuesMethod = parentType.baseType(typeOf[Enumeration].typeSymbol).decl(TermName("values")).asMethod
val obj = mirror.reflectModule(parentType.termSymbol.asModule).instance
mirror.reflect(obj).reflectMethod(valuesMethod)().asInstanceOf[E#ValueSet].head
}
def convert(tpe: Type): TypeTag[_] = {
TypeTag.apply(
runtimeMirror(getClass.getClassLoader),
new TypeCreator {
override def apply[U <: Universe with Singleton](m: api.Mirror[U]) = {
tpe.asInstanceOf[U # Type]
}
}
)
}
def isCaseClass(t: Type) = {
t.companion.decls.exists(_.name.decodedName.toString == "apply") &&
t.decls.exists(_.name.decodedName.toString == "copy")
}
}
而且,当你想使用它时,你可以调用:
val user = Maker[User]
val user2 = Maker[User].copy(email = "someemail@email.com")
以上代码生成任意且唯一的值。它们并不是完全随机的。 API 非常好。考虑到它使用反射,最适合在测试中使用。
在此处阅读我们的完整博客 post:https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html