Я отвечаю на свой вопрос, чтобы предоставить базовое решение, но я также ищу альтернативы и улучшения.
Один вариант, также совместимый с Java и не ограничивающийся классами дел, должен использовать ParaNamer .В Scala другой вариант - проанализировать байты ScalaSig
, прикрепленные к сгенерированным файлам классов.Оба решения не будут работать в REPL.
Вот моя попытка извлечь имена полей из ScalaSig
(который использует scalap и Scala 2.8.1):
def valNames[C: ClassManifest]: Seq[(String, Class[_])] = {
val cls = classManifest[C].erasure
val ctors = cls.getConstructors
assert(ctors.size == 1, "Class " + cls.getName + " should have only one constructor")
val sig = ScalaSigParser.parse(cls).getOrElse(error("No ScalaSig for class " + cls.getName + ", make sure it is a top-level case class"))
val classSymbol = sig.parseEntry(0).asInstanceOf[ClassSymbol]
assert(classSymbol.isCase, "Class " + cls.getName + " is not a case class")
val tableSize = sig.table.size
val ctorIndex = (1 until tableSize).find { i =>
sig.parseEntry(i) match {
case m @ MethodSymbol(SymbolInfo("<init>", owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == 0 => true
case _ => false
}
case _ => false
}
}.getOrElse(error("Cannot find constructor entry in ScalaSig for class " + cls.getName))
val paramsListBuilder = List.newBuilder[String]
for (i <- (ctorIndex + 1) until tableSize) {
sig.parseEntry(i) match {
case MethodSymbol(SymbolInfo(name, owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == ctorIndex => paramsListBuilder += name
case _ =>
}
case _ =>
}
}
paramsListBuilder.result zip ctors(0).getParameterTypes
}
Отказ от ответственности: я не совсем понимаю структуру ScalaSig, и это следует рассматривать как эвристику. В частности, этот код делает следующие предположения:
- Классы дел имеют толькоодин конструктор.
- Запись подписи в нулевой позиции всегда является
ClassSymbol
. - Соответствующим конструктором класса является первый
MethodEntry
с именем <init>
, владелец которого имеетid 0. - Имена параметров имеют в качестве владельца запись конструктора и всегда после этой записи.
Сбой (из-за отсутствия ScalaSig
) во вложенных классах case.
Этот метод также возвращает только Class
экземпляров, а не Manifest
с.
Пожалуйста, не стесняйтесь предлагать улучшения!