我想做一些或多或少的事情归结为以下几点:
def foo[S](x: String): S = S(x) // S(x) does not compile
如果我有:
case class S1(x:String)
case class S2(x:String)
...
case class Sn(x:String)
我可以写foo[Sx]("bar")
来获取Sx("foo")
。
有没有办法指定一个类的实例应该可以从另一个实例(在本例中为String)中构造,并以通用方式实际调用构造函数?
答案 0 :(得分:2)
您可以使用反射(请参阅@Ben Reich的答案以获得详细答案)
def foo[S:ClassTag](x: String): S = {
val runtimeClass = implicitly[ClassTag[S]].runtimeClass
val constructor = runtimeClass.<get a constructor with single String argument>
constructor(x) .asInstanceOf[S]
}
或者可以构造实例的类型类:
trait CanConstruct[S] {
def apply(x:String):S
}
def foo[S:CanConstruct](x: String): S = {
val constructor = implicitly[CanConstruct[S]]
constructor(x).asInstanceOf[S]
}
UPD 您希望构建的每种类型都需要类型类的实例:
implicit val s1constructor = new CanConstruct[S1] { def apply(x:String) = S1(x) }
...
转换函数似乎也是如此:
implicit val convertStringToS1 = S1(_)
implicit val convertStringToS2 = S2(_)
...
答案 1 :(得分:2)
使用反射:
import reflect.ClassTag
def foo[S: ClassTag](x: String) = implicitly[ClassTag[S]]
.runtimeClass
.getConstructors
.map(a => a -> a.getParameters)
.collectFirst {
case (constructor, Array(p)) if p.getType == classOf[String]
=> constructor.newInstance(x).asInstanceOf[S]
}
如果找到合适的构造函数,将返回Option[S]
。
答案 2 :(得分:0)
我用宏来解决它:
object Construct {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def construct[A,B](x:B):A = macro constructImpl[A,B]
def constructImpl[A: c.WeakTypeTag,B](c:Context)(x:c.Expr[B]) = {
import c.universe._
c.Expr[A](q"""new ${c.weakTypeOf[A]}(${x.tree})""")
}
}
我现在可以写下这样的内容:
case class Foo(x:String)
case class Bar(x:Int)
construct[Foo,String]("foo")
construct[Bar,Int](42)
尽管如此,找到避免编写第二个类型参数的方法会很好。