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