从参数列表中实例化案例类

时间:2010-11-27 09:33:33

标签: scala

假设:

case class Foo(a: Int, b: String, c: Double)
你可以说:

val params = Foo(1, "bar", 3.14).productIterator.toList

并获得:

params: List[Any] = List(1, bar, 3.14)

有没有办法“倒退”并直接从这个列表中重新创建一个Foo对象,即:

Foo.createFromList(params)   // hypothetical

而不是写作:

Foo(params(0).asInstanceOf[Int], params(1).asInstanceOf[String], params(2).asInstanceOf[Double])

编辑:它似乎归结为能够将列表的元素作为参数发送到函数而不明确地将它们写出来,例如:

def bar(a: Int, b: Int, c: Int) = //...
val list = List(1, 2, 3, 4, 5)
bar(list.take(3)) // hypothetical, instead of:
bar(list(0), list(1), list(2))

我希望能够做到:

bar(list.take(3): _*)

但这似乎不起作用。

编辑:解决方案基于临时演员的答案,但直接调用构造函数而不是使用apply方法:

case class Foo(a: Int = 0, b: String = "bar", c: Double = 3.14) {
    val cs = this.getClass.getConstructors
    def createFromList(params: List[Any]) =
    cs(0).newInstance(params map { _.asInstanceOf[AnyRef] } : _*).asInstanceOf[Foo]
}

现在你可以做到:

scala> Foo().createFromList(List(4, "foo", 9.81))
res13: Foo = Foo(4,foo,9.81)

您还可以将创建方法重构为特征:

trait Creatable[T <: Creatable[T]] {
    val cs = this.getClass.getConstructors
    def createFromList(params: List[Any]) =
        cs(0).newInstance(params map { _.asInstanceOf[AnyRef] } : _*).asInstanceOf[T]   
}

case class Bar(a: Int = 0, b: String = "bar", c: Double = 3.14) extends Creatable[Bar]

并做例如:

scala> val bar = Bar()
bar: Bar = Bar(0,bar,3.14)

scala> bar == bar.createFromList(bar.productIterator.toList)
res11: Boolean = true

4 个答案:

答案 0 :(得分:54)

scala> case class Foo(a: Int, b: String, c: Double)
defined class Foo

scala> val params = Foo(1, "bar", 3.14).productIterator.toList
params: List[Any] = List(1, bar, 3.14)

scala> Foo.getClass.getMethods.find(x => x.getName == "apply" && x.isBridge).get.invoke(Foo, params map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[Foo]
res0: Foo = Foo(1,bar,3.14)

scala> Foo(1, "bar", 3.14) == res0
res1: Boolean = true

编辑:顺便说一句,到目前为止,语法只是为了提供元组作为参数而跳舞:

scala> case class Foo(a: Int, b: String, c: Double)
defined class Foo

scala> Foo.tupled((1, "bar", 3.14))                
res0: Foo = Foo(1,bar,3.14)

答案 1 :(得分:14)

嗯,你当然可以用元组来做到这一点:

(Foo _).tupled apply (1, bar, 3.14)

但是List[S]没有真正的方法可以从(A, B, C)转到A, B, C <: S。当然可以通过 HList s 来实现此目的

答案 2 :(得分:11)

你可以使用模式匹配,如:

params match {                                   
 case List(x:Int, y:String, d:Double) => Foo(x,y,d)
}

答案 3 :(得分:0)

另一种使用case类伙伴对象的curer方法,并且完全忽略类型安全性:)

scala> case class Foo(a: Int, b: String, c: Double)
defined class Foo

scala> val lst = List(1, "bar", 3.14)
lst: List[Any] = List(1, bar, 3.14)

scala> val foo = lst.foldLeft(Foo.curried: Any){case (r, v) => r.asInstanceOf[Function[Any, _]](v) }.asInstanceOf[Foo]
foo: Foo = Foo(1,bar,3.14)