สมมติว่าเราต้องการเขียนแมโครที่กำหนดคลาสนิรนามด้วยสมาชิกประเภทหรือวิธีการแล้วสร้างอินสแตนซ์ของคลาสนั้นที่พิมพ์แบบคงที่เป็นชนิดโครงสร้างกับวิธีการเหล่านั้นเป็นต้นซึ่งเป็นไปได้กับระบบมาโครใน 2.10 0 และส่วนสมาชิกประเภทนั้นง่ายมาก:
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def foo(name: String): Any = macro foo_impl
def foo_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
}
(ในกรณีที่ReflectionUtils
เป็นลักษณะความสะดวกสบายที่ให้ฉันconstructor
วิธี.)
แมโครนี้ให้เราระบุชื่อของสมาชิกประเภทของคลาสที่ไม่ระบุชื่อเป็นตัวอักษรสตริง:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
โปรดทราบว่ามันถูกพิมพ์อย่างเหมาะสม เราสามารถยืนยันได้ว่าทุกอย่างทำงานได้อย่างที่คาดไว้:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
ทีนี้สมมติว่าเราพยายามทำสิ่งเดียวกันด้วยวิธีการ:
def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
DefDef(
Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
c.literal(42).tree
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
แต่เมื่อเราลองเราไม่ได้รับโครงสร้าง:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
แต่ถ้าเราใส่คลาสที่ไม่ระบุชื่อแบบพิเศษเข้าไป:
def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
val wrapper = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
DefDef(
Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
c.literal(42).tree
)
)
)
),
ClassDef(
Modifiers(Flag.FINAL), wrapper, Nil,
Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
),
Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
))
}
มันได้ผล:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
นี้จะมีประโยชน์อย่างมากมันช่วยให้คุณทำสิ่งต่างๆเช่นนี้เช่น แต่ผมไม่เข้าใจว่าทำไมมันทำงานและประเภทสมาชิกรุ่นงาน bar
แต่ไม่ได้ ฉันรู้ว่าสิ่งนี้อาจไม่ได้ถูกกำหนดพฤติกรรมแต่มันทำให้รู้สึกใด ๆ ? มีวิธีที่สะอาดกว่าในการรับชนิดโครงสร้าง (ด้วยวิธีการ) จากแมโครหรือไม่