Получение имен полей классов case, определенных в объекте, заключенном в другой объект
У меня есть два класса case, определенные следующим образом
object Outer {
case class OuterCase(outerParam: Int)
object Inner {
case class InnerCase(innerParam: Int)
}
}
и я хотел бы получить имена полей их экземпляров, как это:
import scala.tools.nsc.interpreter.ProductCompletion
object EncapsulatedCase extends App {
val outer = Outer.OuterCase(1)
println("outer: " + new ProductCompletion(outer).caseNames)
val inner = Outer.Inner.InnerCase(2)
println("inner: " + new ProductCompletion(inner).caseNames)
}
Это работает, как и ожидалось для объекта outer
, но не для inner
, Вот вывод:
outer: List(outerParam)
inner: List()
Единственное отличие, которое я вижу, в том, что класс InnerCase
определяется в самом объекте, определенном в другом объекте, тогда как OuterCase
заключен только в один объект
Почему это будет проблемой? Почему я могу получить имена параметров outer
но не inner
объект дела?
Я наблюдал такое поведение со Scala 2.9.2.
Спасибо!
Некоторые дополнительные заметки
Я заметил, что запуск одинаковых скомпилированных классов из командной строки scala
или с SBT отличаются.
Из командной строки:
julien@minare:~/prog/testing$ scala -version
Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL
julien@minare:~/prog/testing$ scala -cp target/scala-2.9.2/classes:/Users/julien/.sbt/boot/scala-2.9.2/lib/scala-library.jar:/Users/julien/.ivy2/cache/org.scala-lang/scalap/jars/scalap-2.9.2.jar:
/Users/julien/.sbt/boot/scala-2.9.2/lib/scala-compiler.jar org.example.EncapsulatedCase
outer: List(outerParam)
inner: List()
Но с SBT:
> scala-version
[info] 2.9.2
> sbt-version
[info] 0.11.2
> show external-dependency-classpath
[info] ArrayBuffer(Attributed(/Users/julien/.sbt/boot/scala-2.9.2/lib/scala-library.jar), Attributed(/Users/julien/.ivy2/cache/org.scala-lang/scalap/jars/scalap-2.9.2.jar), Attributed(/Users/julien/.sbt/boot/scala-2.9.2/lib/scala-compiler.jar))
[success] Total time: 0 s, completed Apr 24, 2012 9:20:21 AM
> run-main org.example.EncapsulatedCase
[info] Running org.example.EncapsulatedCase
outer: List()
inner: List()
Любой намек на выяснение того, что здесь происходит, будет принята с благодарностью.
1 ответ
Я не нашел объяснения тому, что происходит, но я нашел способ обойти это с помощью отражения Java.
Поэтому я все равно буду очень признателен за ответ на вопрос ProductCompletion
Поведение
Вот попытка описать поля классов, объявленные в основном конструкторе. В этом нет ничего нового и не гарантируется работа. Ниже я объясню, почему.
def caseNameTypeValues(a: AnyRef) = caseFields(a).map{field => (field.getName, field.getType, field.get(a))}
def caseFields(a: AnyRef) = a.getClass.getDeclaredFields.toSeq.filterNot(_.isSynthetic).take(numConstructorParams(a)).map{field =>
field.setAccessible(true)
field
}
def numConstructorParams(a: AnyRef) = a.getClass.getConstructors()(0).getParameterTypes.size
numConstructorParams(AnyRef)
дает номер N
параметров в первый конструктор, возвращаемый getConstructors()
, а также caseFields(AnyRef)
возвращает первое N
поля, возвращаемые getDeclaredFields()
, caseNameTypeValues(AnyRef)
сопоставляет эти поля с их именами, типами и значениями.
ВНИМАНИЕ: это не гарантируется, так как массивы возвращаются getConstructors()
а также getDeclaredFields()
методы "не сортируются и не располагаются в определенном порядке" в соответствии с API Java.
Если вам повезет, это даст ожидаемый результат:
object Outer {
case class OuterCase(outerParam: Int)
object Inner {
case class InnerCase(innerParam: Int)
}
}
println(caseNameTypeValues(Outer.OuterCase(1)))
println(caseNameTypeValues(Outer.Inner.InnerCase(2)))
выходы
ArrayBuffer((outerParam,int,1))
ArrayBuffer((innerParam,int,2))