[英]Scala: specifying a generic type that is constructible from another
我想做的事情或多或少歸結為以下幾點:
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),並以通用方式實際調用構造函數?
您可以使用反射(有關詳細答案,請參見@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(_) ...
使用反射:
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]
。
我用一個宏解決了它:
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)
不過,找到避免寫第二種類型參數的方法將是不錯的選擇。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.