如何构建通用类型的匿名实例?
How can I build an anonymous instance of a generic type?
考虑这个简单的问题 class:
class MyClass(p: (String, String) *) {
val params: Map[String, String] = p.toMap
}
还有一个扩展它的 class:
class SomeOtherClass(p: (String, String) *) extends MyClass(p: _*)
我想从 MyClass
中删除构造函数,以避免在扩展它的所有内容(可能有很多类型)中携带初始化参数。相反,我更喜欢扩展 MyClass
的类型的伴随对象继承某种构建器方法。
class MyClass {
val param = Map.empty[String, String] // some member with a default value
}
trait MyClassBuilder[A <: MyClass] {
def apply(p: (String, String) *): A = ???
}
基本上 apply
应该这样做:
new A {
override val param = p.toMap
}
显然,上面的代码是行不通的。这个想法是这样使用子类型:
class SomeOtherClass extends MyClass
object SomeOtherClass extends MyClassBuilder[SomeOtherClass] {
// object specific things..
}
然后 SomeOtherClass
将依赖继承的 apply
方法来创建其自身的实例。反射或宏似乎可以实现类似的功能,但我真的不知道。明确地说,我希望像 SomeOtherClass
这样的客户端代码根本不需要构造函数,这就是为什么我想探索在泛型上创建匿名 classes 的原因。生成的可能不是 SomeOtherClass
,它可能是扩展 MyClass
.
的任何内容
具有以下特征的类型;
trait MyType {
def params: Map[String, String]
}
伴随对象可以创建一个实例:
object MyType {
def apply(pairs: (String, String)*): MyType = new MyType {
val params = pairs.toMap
}
}
//allows: MyType("a" -> "b", "c" -> "d")
您可以像这样在子class中添加一个非参数构造函数:
class SomeOtherClass(p: (String, String)*) extends MyClass(p: _*) {
def this() = this(somedefaultvalueofp)
}
然后将伴生对象与 apply 一起使用以应用来自外部的值:
object SomeOtherClass {
def apply(p: (String, String)*): SomeOtherClass = new SomeOtherClass(p)
}
********** 已编辑 ****************
我明白了这个要求。因此,您可以在 MyClass
:
上添加非参数构造函数
class MyClass(p: (String, String)*) {
def this() = this(defaultvalueofp)
}
并像您提到的那样创建一个 class 生成器:
class MyClassBuilder[T <: MyClass] {
def apply(p: (String, String)*): T = new T(p)
}
这可能对您有帮助:
import scala.reflect._
abstract class MyClassBuilder[A <: MyClass: ClassTag] {
def apply() = classTag[A].runtimeClass.newInstance.asInstanceOf[A]
}
您可能会发现我没有在这里传递 (String, String) *
因为它们对于实例化来说是多余的。无论如何,您可以在此处访问整个 A
的实例,因此至少您可以在实例化后更改 param
。这不完全是您想要的 - 因为您似乎正在寻找一种在运行时额外扩展 SomeOtherClass
的方法。但是,在 Scala 中运行时创建新类型是不可能的,但您可以将其视为原型 - 只需获取 SomeOtherClass 实例并在 apply()
:
中变异一次
import scala.reflect._
object Ctx {
class MyClass {
private[Ctx] var _param = Map.empty[String, String]
def param = _param
}
abstract class MyClassBuilder[A <: MyClass: ClassTag] {
def apply(p: (String, String) *) = {
val i = classTag[A].runtimeClass.newInstance.asInstanceOf[A]
i._param = Map(p: _*)
i
}
}
}
scala> class MySpecialClass extends Ctx.MyClass
defined class MySpecialClass
scala> object MySpecialBuilder extends Ctx.MyClassBuilder[MySpecialClass]
defined module MySpecialBuilder
scala> MySpecialBuilder("A" -> "b")
res12: MySpecialClass = MySpecialClass@2871ed4a
scala> res12.param
res13: scala.collection.immutable.Map[String,String] = Map(A -> b)
否则你必须处理编译时反射。
一个有趣的替代方案是 extensible records (Shapeless2) - 它们实际上允许您逐个构建新类型,但您必须在那里处理 HList
而不是 class :
import shapeless._ ; import syntax.singleton._ ; import record._
import shapeless.ops.record.Updater
import scala.reflect.runtime.universe._
val param = Witness("param")
val someFunW = Witness("someFun")
//defining classess (instead of "class MyClass")
type Param = Map[String, String] with KeyTag[param.T, Map[String, String]]
type SomeFun = (String => String) with KeyTag[someFunW.T, (String => String)]
type MyClass = Param :: HNil
type MySpecialClass = SomeFun :: MyClass
def someFun(s: String) = s + "A"
//defining default values (instead of "val param = ...")
def newMyClass[T <: HList : TypeTag]: T = (
if (typeTag[T] == typeTag[MyClass])
(param ->> Map.empty[String, String]) :: HNil
else if (typeTag[T] == typeTag[MySpecialClass])
(someFunW ->> someFun _) :: newMyClass[MyClass]
else HNil
).asInstanceOf[T]
//Defining builder
def buildWithParam[T <: HList: TypeTag](p: Map[String, String])(implicit ev: Updater[T, Param])
= newMyClass[T] + ("param" ->> p)
scala> buildWithParam[MySpecialClass](Map("a" -> "v"))
res6: ... = <function1> :: Map(a -> v) :: HNil
scala> res6("someFun").apply("a")
res7: String = aA
scala> buildWithParam[MyClass](Map("a" -> "v"))
res8: ... = Map(a -> v) :: HNil
考虑这个简单的问题 class:
class MyClass(p: (String, String) *) {
val params: Map[String, String] = p.toMap
}
还有一个扩展它的 class:
class SomeOtherClass(p: (String, String) *) extends MyClass(p: _*)
我想从 MyClass
中删除构造函数,以避免在扩展它的所有内容(可能有很多类型)中携带初始化参数。相反,我更喜欢扩展 MyClass
的类型的伴随对象继承某种构建器方法。
class MyClass {
val param = Map.empty[String, String] // some member with a default value
}
trait MyClassBuilder[A <: MyClass] {
def apply(p: (String, String) *): A = ???
}
基本上 apply
应该这样做:
new A {
override val param = p.toMap
}
显然,上面的代码是行不通的。这个想法是这样使用子类型:
class SomeOtherClass extends MyClass
object SomeOtherClass extends MyClassBuilder[SomeOtherClass] {
// object specific things..
}
然后 SomeOtherClass
将依赖继承的 apply
方法来创建其自身的实例。反射或宏似乎可以实现类似的功能,但我真的不知道。明确地说,我希望像 SomeOtherClass
这样的客户端代码根本不需要构造函数,这就是为什么我想探索在泛型上创建匿名 classes 的原因。生成的可能不是 SomeOtherClass
,它可能是扩展 MyClass
.
具有以下特征的类型;
trait MyType {
def params: Map[String, String]
}
伴随对象可以创建一个实例:
object MyType {
def apply(pairs: (String, String)*): MyType = new MyType {
val params = pairs.toMap
}
}
//allows: MyType("a" -> "b", "c" -> "d")
您可以像这样在子class中添加一个非参数构造函数:
class SomeOtherClass(p: (String, String)*) extends MyClass(p: _*) {
def this() = this(somedefaultvalueofp)
}
然后将伴生对象与 apply 一起使用以应用来自外部的值:
object SomeOtherClass {
def apply(p: (String, String)*): SomeOtherClass = new SomeOtherClass(p)
}
********** 已编辑 ****************
我明白了这个要求。因此,您可以在 MyClass
:
class MyClass(p: (String, String)*) {
def this() = this(defaultvalueofp)
}
并像您提到的那样创建一个 class 生成器:
class MyClassBuilder[T <: MyClass] {
def apply(p: (String, String)*): T = new T(p)
}
这可能对您有帮助:
import scala.reflect._
abstract class MyClassBuilder[A <: MyClass: ClassTag] {
def apply() = classTag[A].runtimeClass.newInstance.asInstanceOf[A]
}
您可能会发现我没有在这里传递 (String, String) *
因为它们对于实例化来说是多余的。无论如何,您可以在此处访问整个 A
的实例,因此至少您可以在实例化后更改 param
。这不完全是您想要的 - 因为您似乎正在寻找一种在运行时额外扩展 SomeOtherClass
的方法。但是,在 Scala 中运行时创建新类型是不可能的,但您可以将其视为原型 - 只需获取 SomeOtherClass 实例并在 apply()
:
import scala.reflect._
object Ctx {
class MyClass {
private[Ctx] var _param = Map.empty[String, String]
def param = _param
}
abstract class MyClassBuilder[A <: MyClass: ClassTag] {
def apply(p: (String, String) *) = {
val i = classTag[A].runtimeClass.newInstance.asInstanceOf[A]
i._param = Map(p: _*)
i
}
}
}
scala> class MySpecialClass extends Ctx.MyClass
defined class MySpecialClass
scala> object MySpecialBuilder extends Ctx.MyClassBuilder[MySpecialClass]
defined module MySpecialBuilder
scala> MySpecialBuilder("A" -> "b")
res12: MySpecialClass = MySpecialClass@2871ed4a
scala> res12.param
res13: scala.collection.immutable.Map[String,String] = Map(A -> b)
否则你必须处理编译时反射。
一个有趣的替代方案是 extensible records (Shapeless2) - 它们实际上允许您逐个构建新类型,但您必须在那里处理 HList
而不是 class :
import shapeless._ ; import syntax.singleton._ ; import record._
import shapeless.ops.record.Updater
import scala.reflect.runtime.universe._
val param = Witness("param")
val someFunW = Witness("someFun")
//defining classess (instead of "class MyClass")
type Param = Map[String, String] with KeyTag[param.T, Map[String, String]]
type SomeFun = (String => String) with KeyTag[someFunW.T, (String => String)]
type MyClass = Param :: HNil
type MySpecialClass = SomeFun :: MyClass
def someFun(s: String) = s + "A"
//defining default values (instead of "val param = ...")
def newMyClass[T <: HList : TypeTag]: T = (
if (typeTag[T] == typeTag[MyClass])
(param ->> Map.empty[String, String]) :: HNil
else if (typeTag[T] == typeTag[MySpecialClass])
(someFunW ->> someFun _) :: newMyClass[MyClass]
else HNil
).asInstanceOf[T]
//Defining builder
def buildWithParam[T <: HList: TypeTag](p: Map[String, String])(implicit ev: Updater[T, Param])
= newMyClass[T] + ("param" ->> p)
scala> buildWithParam[MySpecialClass](Map("a" -> "v"))
res6: ... = <function1> :: Map(a -> v) :: HNil
scala> res6("someFun").apply("a")
res7: String = aA
scala> buildWithParam[MyClass](Map("a" -> "v"))
res8: ... = Map(a -> v) :: HNil