在Scala中使用字符串识别对象字段

时间:2019-05-31 20:30:08

标签: scala ecmascript-6

在ES6中,我们可以将对象的字段称为:

seller['firstname']  

代表

seller.firstname

在Scala中有没有办法做同样的事情?我的意思是,要使用字符串来引用对象字段吗?

3 个答案:

答案 0 :(得分:6)

使用Scala 2.13,您应该能够使用案例类的新productElementNames方法,在其字段名称上返回一个迭代器,从而或多或少地实现相关性。

通过用productIterator获得的字段值压缩字段名称,我们可以使用字段名称的字符串版本来访问案例类字段值:

implicit class CaseClassExtensions(obj: Product) {

  def select[T](field: String): Option[T] =
    (obj.productElementNames zip obj.productIterator)
      .collectFirst { case (`field`, value) => value.asInstanceOf[T] } 
}
// case class Seller(firstName: String, lastName: String)
Seller("Hello", "World").select[String]("firstName")
// Option[String] = Some(Hello)

这里,隐式类用于丰富Product类型(这是case类的继承类型),以便在任何case类上调用此select方法。


但是与您在Java语言中习惯的相反:

  • 这仅限于案例类。
  • 这将返回值的Option(否则,当您尝试获取未定义的字段的值时必须处理异常)。
  • 您必须指定关联值的返回类型。

答案 1 :(得分:2)

不。我不这么认为。作为基于JVM的Scala,它需要静态类型。除非您想使用reflection

答案 2 :(得分:1)

Xavier Guihot的解决方案将在 Scala 2.13 中运行,但是您也可以在 Scala 2.12 中使用 shapeless 实现按字符串访问。 >或以下。

首先,将 shapeless 添加到您的 build.sbt

libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"

我们需要使用 LabelledGeneric ToMap 。我们可以将其实现为适用于案例类的扩展类:

object DynamicAccess {
  implicit class DynamicAccessOps[R <: HList, T <: Product with Serializable](o: T)(
    implicit gen: LabelledGeneric.Aux[T, R],
    toMap: ToMap[R]
  ) {
    private lazy val fields = toMap(gen.to(o))
    def apply[S](field: String): Option[S] = fields
                              .get(Symbol.apply(field).asInstanceOf[toMap.Key])
                              .asInstanceOf[Option[S]]
  }
}

然后您可以像这样使用它:

import DynamicAccess._

case class Foo(bar: String, baz: String)

val f = Foo("x", "y")

f[String]("bar") // Some(x)
f[String]("baz") // Some(y)
f[String]("foobar") //None