Scala:反对命名参数

时间:2018-03-27 23:28:07

标签: scala reflection arguments

我尝试将命名参数传递给来自常规Scala对象(如string / list / map)的函数,其中参数的名称及其值都是可变的(在我的情况下来自解析)用户输入)。有没有办法在Scala中执行此操作?我主要在scala中寻找一个简短的程序,类似于python:

def surprise(animal, color):
    print('Oh, a ' + color + ' ' + animal + '!')

arguments = {'animal': 'cat', 'color': 'black'}
surprise(**arguments)

由于python可以将字典解包为命名参数,因此会产生

Oh, a black cat!

我一直在scala中搜索此功能,但我找不到它。任何人都可以举例说明如何在scala中实现这个目标吗?

提前致谢!

1 个答案:

答案 0 :(得分:1)

我想说,这并不像python那么容易,但我会尝试提出几种解决方案。您需要从类型和订单的json(或其他用户输入)中提取参数。例如,可以使用helper case class和play-json:

来完成
def surprise(animal: String, color: String): Unit = {
  println(s"Oh, a $color $animal!")
}

import play.api.libs.json.{JsValue, Json}

case class FuncArguments(animal: String, color: String)
implicit val funcArgumentsFormat = Json.format[FuncArguments]

implicit def jsValueToFuncArguments(json: JsValue): FuncArguments =
  json.as[FuncArguments]

def surprise2(json: JsValue): Unit = {
  (surprise _).tupled(FuncArguments.unapply(json).get)
}

案例类与您的方法具有相同的签名。 implicit val funcArgumentsFormat是play-json格式,用于将数据提取到case类中(不安全,因为as。如果在json中缺少必需的参数名称/类型,将抛出异常),隐式def jsValueToFuncArguments转换你的json进入案例类(理论上也是不安全的,因为Option.get,但不认为你可以在这里得到异常)。并帮助函数surprise2将json转换为参数。

另一种方法是,例如,使用一些反射:

import play.api.libs.json.{JsValue, Json}

class SomeClass {
  def surprise(animal: String, color: String): Unit = {
    println(s"Oh, a $color $animal!")
  }
}

def surprise3(json: JsValue): Unit = {
  val method = classOf[SomeClass].getMethods.find(_.getName == "surprise").get
  val params = method.getParameters
  val values = params.map(_.getName).map(name => (json \ name).as[String])
  val ref = new SomeClass
  method.invoke(ref, values: _*)
}

在这个例子中,我们假设所有字段都具有相同的类型String,您的函数在类中。 Play-json用于解析。获取方法而不是参数名称,而不是提取参数的值(与参数的顺序相同),而不仅仅是将它们应用于函数。

并致电:

val json = Json.obj("animal" -> "cat", "color" -> "black")

surprise2(json)
surprise3(json)