繁体   English   中英

Scala:指定可从另一个构造的通用类型

[英]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),并以通用方式实际调用构造函数?

  1. 您可以使用反射(有关详细答案,请参见@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] } 
  2. 或可以构造实例的类型类:

     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) } ... 
  3. 转换函数似乎也是如此:

     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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM