scala + annotation + reflection

Posted 2010年12月17日 by

Scalaで、アノテーションを読みたくていろいろ試行錯誤していると、ネットで、
Scalaで作られたクラスをダンプするコードを見つけました。
オリジナルのコードでは読み取れる情報が少ないので、機能拡張したのが以下のコードです。

チェックしたいScalaクラスをListの要素に書けば、フィールドとメソッドをreflectして、アノテーションを読みだしてくれます。


package seedo

import scala.collection.JavaConversions._
object  DumpClass {
  def check() {
    List(
    	    classOf[seedo.MASTER]
    	  , classOf[seedo.MASTER1]
    ).foreach(cl =>
      {
        println(cl.getName)
        // Field
        cl.getDeclaredFields.foreach(f => {
          val annotations = java.util.Arrays.asList(f.getAnnotations: _*).map("@" + _.annotationType.getSimpleName).mkString(" ");
          println(" field " + annotations + " [" + f.getName + "] GenericType [" + f.getGenericType + "] [" + f.getType.getSimpleName + "]")
          f.getDeclaredAnnotations.foreach(g => {
        	  println("  fieldAnnotation [" + g.annotationType + "] SimpleName [" + g.annotationType.getSimpleName+ "]")
          })
        })
        // Methods
        cl.getDeclaredMethods.foreach(m => {          
          val annotations = java.util.Arrays.asList(m.getReturnType.getAnnotations: _*).map("@" + _.annotationType.getSimpleName).mkString(" ");
          println(" method [" + annotations + "] [" + m.getName + "]")
          m.getParameterTypes.foreach(p =>{
        	  println("  ParameterType [" + p + "] [" + p.getSimpleName + "]")
          })
        })
        println
      })
  }
  def main(args : Array[String]) : Unit = {
	  println("DumpClass World ----")
	  check
  }
}


以前、Scalaで作った Bean クラスのMASTERクラスを指定して実行すると、以下のようにダンプします。

DumpClass World
seedo.MASTER
 field  [TABLE] GenericType [class java.lang.String] [String]
 field  [PRIMARY_KEY] GenericType [class java.lang.String] [String]
 field  [timestamp_COLUMN] GenericType [class java.lang.String] [String]
 field  [RENTALODRID] GenericType [class java.math.BigDecimal] [BigDecimal]
 field  [CUSTOMERID] GenericType [class java.lang.Integer] [Integer]
・・・
 method [] [RENTALODRID]
 method [] [CUSTOMERID]
 method [] [DLVTYPCODE]
・・・


さらに、このBeanクラスに@でJavaで定義したアノテーションを付けて、DumpClassクラスで使えばクラス情報を読むことができます。

class MASTER1 {
	@Id var RENTALODRID:BigDecimal = null
	@Column var CUSTOMERID:Integer = null
	@Column var SALESPRICE:BigDecimal = null
	@Column var DLVFEETAX:BigDecimal = null
	@Column var DLVFEE:BigDecimal = null
 ・・・


次の、MASTER2クラスのようにコンストラクタ引数で、メンバーを宣言した場合、

ここのメンバーに@アノテーションを付けても、この部分を読み取ることができません。

Javaクラスのようにクラスのなかのメンバーしかダメです。

class MASTER2 (
	@BeanProperty @Id var RENTALODRID:BigDecimal
	, @BeanProperty var CUSTOMERID:Integer
	, @BeanProperty var SALESPRICE:BigDecimal
	, @BeanProperty var DLVFEETAX:BigDecimal
 ・・・


それと、Scalaで定義したアノテーションは読むことができないです。

現状はScalaの処理系の中からしか使えないのでしょう。

アノテーションは、やはりJavaで作らないとダメです。

Javaでアノテーションを定義するには、JavaでClassファイルを作って、Jar化してScalaのビルドパスに追加します。

Javaでふつうに、Annotation を定義すればいいです。


package seedo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD})
public @interface Id { }


package seedo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE })
public @interface Table {
	String name();
	String value();
}


package seedo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD})
public @interface Column {}


ClassDumpクラスに、以下のコードを コード下にある単独println の上の行に挿入すると、Javaで定義したTableアノテーションのname値とvalue値が読めます。

       // Static Annotation
        val annotations = java.util.Arrays.asList(cl.getAnnotations: _*).map("@" + _.annotationType.getSimpleName).mkString(" ")
        cl.getAnnotations.foreach(aa => {
        	val an = aa.annotationType.getSimpleName
        	println(" annotation SimpleName=" + an)
        	if("Table".equals(an)){
        		val tb = aa.asInstanceOf[Table]
        		println(" Table =" + tb.name + " value="+ tb.value)
        	}
        })
      	println(" annotation " + annotations)




Scalaのクラスを覗いてみると、細かい違いが見えてきます。

Beanで、ScalaのDouble / Float / Int と java.lang.Double の浮動小数点型をメンバーにして定義すると、

reflectした結果は、Double / Float / IntはClassではないと表示されます。Javaのプリミティブの double / float / int です。
メソッドの引数にIntと定義しても、java.lang.Integerで実際には受け渡ししています。Java6と同じです。


	var RATE:java.lang.Double = null
	var RATE1:Double = 0.0
	var RATE2:Float = 0.0F
	var RATE3:Int = 0

 field  [RATE] GenericType [class java.lang.Double] [Double]
 field  [RATE1] GenericType [double] [double]
 field  [RATE2] GenericType [float] [float]
 field  [RATE3] GenericType [int] [int]


純粋に、Scalaの世界でアノテーションはどうやって使うのでしょう???

Post Details

  • Post Title: scala + annotation + reflection
  • Author: admin
  • Filed As: Scala
  • Tags:
  • You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

コメントを残す