在ES6中,我们可以将对象的字段称为:
seller['firstname']
代表
seller.firstname
在Scala中有没有办法做同样的事情?我的意思是,要使用字符串来引用对象字段吗?
答案 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