scala macro annotation 使用 例子

功能:用 宏注解实现一个 为case class 生成get set 方法 注解

github 地址:https://github.com/1178615156/use-scala-macro-annotation-make-get-set


//功能如下

@MakeGetSet
case class Hello(
                  var a: String,
                  var int: Int,
                  var option: Option[String],
                  var none: Option[AnyRef] = None,
                  val hello: Boolean = true
                  )

object Main {
  def main(args: Array[String]) {
    val a = new Hello("a", 1, Some("option"))
    //a.a
    println(a.getA)//a

    //a.int
    println(a.getInt)//1

    //return option.getOrElse(null)
    println(a.getOption)//option

    //return option.getOrElse(null)
    println(a.getNone)//null

    //a.none=Option(Some(""))
    a.setNone(Some(""))
    println(a.none)//Some(Some())

    //a.none=Some("")
    a.none = Some("")
    println(a.none)//Some()

    println(a.getHello)//true

    //no have this method because hello is val
    //a.setHello(false )
  }
}


宏 的实现代码 :




import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
import scala.annotation.{compileTimeOnly, StaticAnnotation}

/**
 * Created by YuJieShui on 2015/9/4.
 */
@compileTimeOnly("")
class MakeGetSet extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro AnnotationGetSetMacroImpl.impl
}

class AnnotationGetSetMacroImpl(val c: Context) {

  import c.universe._

  //get case class
  def getInCaseClass(list_annottees: List[Tree]) = list_annottees match {
    case caseClass :: Nil => caseClass
    case caseClass :: companionObject :: Nil => caseClass
  }

  def impl(annottees: c.Expr[Any]*): c.Expr[Any] = {
    val inCaseClass = getInCaseClass(annottees.map(_.tree).toList)

    val out: c.universe.Tree = inCaseClass match {
      //if inCaseClass no a case class
      //throw match error
      case q"case class $name(..$params) extends ..$bases { ..$body }" =>

        val list_params: List[ValDef] = params.asInstanceOf[List[ValDef]]

        val get_set_params_func = list_params.map((param: c.universe.ValDef) => {
          //get method name
          val get_name = TermName("get" + {
            val vn = param.name.toString;
            vn.head.toString.toUpperCase() + vn.tail
          })
          //set method name
          val set_name = TermName("set" + {
            val vn = param.name.toString;
            vn.head.toString.toUpperCase() + vn.tail
          })

          param match {
            case ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) =>
              val v_type: c.universe.Tree = tq"$tpt"

              //如果参数类型为泛型参数
              v_type.children.headOption.map {
                // where type is Option[_]
                case tq"Option" =>
                  Tuple2(
                    q"""@..${mods.annotations}
                        def $get_name = $name.orNull
                        """,
                    //where value is var
                    //make set method
                    if (mods.hasFlag(Flag.MUTABLE))
                      q"def $set_name(sss:${v_type.children(1)}):Unit=this.${name}=Option(sss)"
                    else
                      q""
                  )
                //other such : Future[_],List[_]
                case _ =>
                  Tuple2(
                    q"def $get_name = ${param.name}",
                    q""
                  )
              } //非泛型参数
                .getOrElse(Tuple2(
                q"def $get_name= {${param.name}}",
                if (mods.hasFlag(Flag.MUTABLE))
                  q"def $set_name(sss:$v_type):Unit=this.${name}=sss"
                else
                  q""
              ))
          }
        }).flatMap(t2 => List(t2._1, t2._2))

        q"""
            case class $name(..$params) extends ..$bases {
            ..$get_set_params_func
            ..$body
            }
            """
    }
    c.Expr(out)
  }
}

你可能感兴趣的:(scala)