旧ブログ

sambaiz.net に引っ越しました

Scalaのcase classのフィールド名一覧をReflectionで汎用的に取得する

例えば、以下のようなcase classがあったとして、

case class Neko(name: String, age: Int, nyaa: Option[String])

このフィールド名一覧、name, age, nyaaは以下の方法で取得することができます。

println(classOf[Neko].getDeclaredFields.map(_.getName).mkString(","))
-> name,age,nyaa

フィールドを取得するメソッドにはgetDeclaredFieldsのほかにgetFieldsというのもあります。 getFieldsがpublicのものだけを取得するのに対して、getDeclaredFieldsは全て取得します。 getFieldsではなく、getDeclaredFieldsを使っているのはcase classのフィールドがprivateで定義されるからです。 値の取得には同名のメソッドを呼ぶのでしょう。

println(classOf[Neko].getDeclaredFields.mkString(","))
-> private final java.lang.String p.Neko.name,private final int p.Neko.age,private final scala.Option p.Neko.nyaa

println(classOf[Neko].getMethods.mkString(","))
-> public java.lang.String p.Neko.name(), ... ,public scala.Option p.Neko.nyaa(),public int p.Neko.age(), ...

以上を踏まえてフィールド名一覧を汎用的に取得する関数、fieldList[T]を作りたいところですが、

def fieldList[T](): Array[String] = classOf[T].getDeclaredFields.map(_.getName)

これはコンパイル時にエラーが出ます。

class type required but T found

それではどうすればいいかと言うと、Reflectionを使います。

Reflection - 型タグとマニフェスト - Scala Documentation

以下のように暗黙のClassTagパラメータを用意しておくと、ClassTag[T]があればそれを、なければ自動的に生成して、使うことができます。

def fieldList[T](implicit tag: ClassTag[T]): Array[String] = tag.runtimeClass.getDeclaredFields.map(_.getName)

println(fieldList[Neko].mkString(","))
-> name,age,nyaa