Получить список имен полей из класса 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 с помощью
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 должен иметь инициализированные поля.