Идентификация полей объекта со строками в Scala - PullRequest
3 голосов
/ 31 мая 2019

В ES6 мы можем ссылаться на поле объекта как:

seller['firstname']  

для представления

seller.firstname

Есть ли способ сделать то же самое в Scala? Я имею в виду, использовать строки для ссылки на поля объекта?

Ответы [ 3 ]

6 голосов
/ 01 июня 2019

С Scala 2.13 вы сможете сделать что-то более или менее связанное, используя новый * case-класс 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) для вызова этого метода select для любого класса case.


Но вопреки тому, что выиспользуется в Javascript:

  • Это ограничено классами дел.
  • Возвращает Option значения (или вам придется иметь дело с исключениями при попытке получитьзначение поля, которое не определено).
  • Необходимо указать тип возврата связанного значения.
2 голосов
/ 31 мая 2019

Нет.Я так не думаю.В качестве Scala на основе JVM, которые требуют статического типа.Если вы не хотите использовать отражение

1 голос
/ 02 июня 2019

Решение от Xavier Guihot будет работать с Scala 2.13 , но вы также можете реализовать строковый доступ с shapeless с Scala 2.12 или ниже.

Прежде всего, добавьте бесформенный к вашему build.sbt :

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

Нам нужно будет использовать LabelledGeneric и ToMap .Мы могли бы реализовать его как класс расширения, работающий с классами case:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...