Получить список имен полей из класса case

Мне нужно получить только имена полей case-класса. Меня не интересуют его ценности. я думал getClass.getDeclaredFields.map(_.getName) вернул бы список имен полей.

scala> case class User(id: Int, name: String)
defined class User

scala> User.getClass.getDeclaredFields
res14: Array[java.lang.reflect.Field] = Array(public static final User$ User$.MODULE$)

scala> User.getClass.getDeclaredFields.toList
res15: List[java.lang.reflect.Field] = List(public static final User$ User$.MODULE$)

scala> val user = User(1, "dude")
user: User = User(1,dude)

scala> user.getClass.getDeclaredFields.toList
res16: List[java.lang.reflect.Field] = List(private final int User.id, private final java.lang.String User.name)

Что это за пользователь $.MODULE$? Что это такое?

Метод getDeclaredFields прекрасно работает, когда у вас есть экземпляр класса case, но я не хочу создавать экземпляр, чтобы получать только поля.

Почему это не такUser.getClass.getDeclaredFields.map(_.getName) == List("id", "name")?

7 ответов

Решение

Используя User.getClassвы ссылаетесь на объект-компаньон класса, который Scala по умолчанию создает для класса case, а не сам класс case. Чтобы получить объект класса класса case, используйте classOf[User],

В качестве альтернативы вы можете использовать API отражения Scala для получения метаданных класса case, который дает вам гораздо больше информации:

import scala.reflect.runtime.universe._

def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
  case m: MethodSymbol if m.isCaseAccessor => m
}.toList

Тест в консоли sbt:

scala> case class User(name: String, age: Int)
defined class User

scala> classAccessors[User]
res0: List[reflect.runtime.universe.MethodSymbol] = List(value age, value name)

Начало Scala 2.13, case classes (которые являются реализацией Product) теперь снабжены методом productElementNames, который возвращает итератор над именами их полей.

Из экземпляра класса case (скажем, case class Person(name: String, age: Int)), можно получить List его полей:

Person("hello", 28).productElementNames.toList
// List[String] = List(name, age)

Следуя решению Андрея Тюкина, чтобы получить только список полей в Scala 2.12:

val fields: List[String] = classOf[Dummy].getDeclaredFields.map(_.getName).toList

User.getClass не дает вам эквивалент User.class в Java, но он дает вам класс объекта-компаньона User учебный класс. Вы можете получить Class объект User с classOf[User],

редактировать: и User$.MODULE$ является средством доступа к одноэлементному экземпляру, который используется внутри. Думайте об этом как эквивалент MyClass.INSTANCE когда вы пишете синглтоны на Java.

Если вам также интересно, почему некоторый код Scala-отражения больше не компилируется, вот грубое решение с хорошим Java-отражением (которое, очевидно, работает точно так же, начиная примерно с 1300 года до н.э.):

case class User(b: Int, a: String)
val u = User(42, "JohnDoe")

classOf[User]
.getDeclaredFields
.map{ f => 
  f.setAccessible(true)
  val res = (f.getName, f.get(u))
  f.setAccessible(false)
  res
}

Он получит как имена, так и значения:

Array((b,42), (a,bob))

Порядок, похоже, такой же, как в конструкторе.

Извлечение имен полей класса case с помощью

LabelledGeneric Aux pattern

https://svejcar.dev/posts/2019/10/22/extracting-case-class-field-names-with-shapeless/

"Scala 2.13 добавил новый метод, productElementNames, в черту продукта"

... но Shapelessоснованный подход рекомендован автором

Если вы используете Spark, это самый простой способ получить поля:

val cols = Seq(CaseClassModel()).toDF().columns

Примечание. CaseClassModel должен иметь инициализированные поля.

Другие вопросы по тегам