Scala ケースクラスの考察
-
19-09-2019 - |
質問
特定のデバッグ目的で、ケースクラスに混合してそのフィールドを実行時に検査できるようにする特性を (Scala 2.8 で) 書こうとしています。ソースファイルで宣言された順序でそれらを取得し、ケースクラス内の他のフィールドを省略したいと考えています。例えば:
trait CaseClassReflector extends Product {
def getFields: List[(String, Any)] = {
var fieldValueToName: Map[Any, String] = Map()
for (field <- getClass.getDeclaredFields) {
field.setAccessible(true)
fieldValueToName += (field.get(this) -> field.getName)
}
productIterator.toList map { value => fieldValueToName(value) -> value }
}
}
case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector {
val other: Int = 42
}
scala> val c = Colour(234, 123, 23)
c: Colour = Colour(234,123,23)
scala> val fields = c.getFields
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23))
上記の実装には明らかに欠陥があります。なぜなら、プロダクト内のフィールドの位置とその名前の間の関係を、それらのフィールドの値の等価性によって推測するためです。そのため、たとえば、次のような実装は機能しません。
Colour(0, 0, 0).getFields
これを実装する方法はありますか?
解決
は、すべての例で、私はフィールドは逆の順序であり見た:れるGetFields配列の最後の項目は、ケースクラスにリストされた最初のものです。あなたはケースクラスを使用する場合は、「きれい」、そしてあなただけのproductElement(n)
にgetDeclaredFields()( getDeclaredFields.length-n-1)
をマッピングすることができる必要があります。
しかし、私はそれがそのようでなければならないと主張している仕様では何も知らないので、これは、かなり危険である、とあなたはケースクラス内のvalを上書きしている場合は、それも(getDeclaredFieldsでそれを表示されません「)そのスーパークラスのフィールドに現れるでしょう。
あなたは物事がこの方法ですと仮定するようにコードを変更しますが、その名前のgetterメソッドとproductIteratorが同じ値を返すことを確認し、実際にはない知っていることを意味し、彼らは(ない場合は例外がスローされることがあります何がどのような)に対応します。
他のヒント
はトランクに見て、あなたはこれを見つけることができます。コメントを聞いて、これはサポートされていません。しかし、私はまた、これらの名前を必要としているので...
/** private[scala] so nobody gets the idea this is a supported interface.
*/
private[scala] def caseParamNames(path: String): Option[List[String]] = {
val (outer, inner) = (path indexOf '$') match {
case -1 => (path, "")
case x => (path take x, path drop (x + 1))
}
for {
clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer)
ssig <- ScalaSigParser.parse(clazz)
}
yield {
val f: PartialFunction[Symbol, List[String]] =
if (inner.isEmpty) {
case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1)
}
else {
case x: ClassSymbol if x.name == inner =>
val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " "))
xs.toList map (_.name dropRight 1)
}
(ssig.symbols partialMap f).flatten toList
}
}
ここ
上記の例に基づいて、短いと作業バージョンです trait CaseClassReflector extends Product {
def getFields = getClass.getDeclaredFields.map(field => {
field setAccessible true
field.getName -> field.get(this)
})
}
を使用することもできます。 ProductCompletion
インタプリタ パッケージからケース クラスの属性名と値を取得します。
import tools.nsc.interpreter.ProductCompletion
// get attribute names
new ProductCompletion(Colour(1, 2, 3)).caseNames
// returns: List(red, green, blue)
// get attribute values
new ProductCompletion(Colour(1, 2, 3)).caseFields
編集:ローランドとvirtualeyesによるヒント
を含める必要があります。 scalap
の一部であるライブラリ スカラ言語コレクション.
roland さんと virtualeyes さん、ヒントをありがとう。