Получение имен полей классов 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))
Другие вопросы по тегам