2

次のように定義された2つのケースクラスがあります

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)
}

これは objectouterでは期待どおりに機能しますが、 では機能しませんinner。出力は次のとおりです。

outer: List(outerParam)
inner: List()

私が見る唯一の違いは、クラスInnerCaseが別のオブジェクトで定義されたオブジェクト自体で定義されているのに対し、クラスはOuterCase1 つのオブジェクトのみに囲まれていることです。

なぜそれが問題になるのでしょうか?ケース オブジェクトのパラメータ名を取得できるのに、なぜケース オブジェクトを取得でき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()

ここで何が起こっているのかを知るためのヒントをいただければ幸いです。

4

1 に答える 1

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)NgetDeclaredFields()caseNameTypeValues(AnyRef)

getConstructors()警告:およびメソッドによって返される配列は、 Java APIgetDeclaredFields()に従って「ソートされず、特定の順序ではない」ため、これが機能することは保証されません。

運が良ければ、期待される結果が得られます。

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))
于 2012-04-25T12:45:11.700 に答える