Подтвердить что ты не робот

Получить список имен полей из класса 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")?

4b9b3361

Ответ 1

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

В качестве альтернативы вы можете использовать Scala API отражения, чтобы получить метаданные класса 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)

Ответ 2

Начиная с 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)

Ответ 3

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

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

Ответ 4

Если вам также интересно, почему некоторый код 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))

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

Ответ 5

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

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

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