反射和宏共享很多的API,因爲它們基本上是一樣的東西:元編程。您可以生成並執行代碼,您必須反映類型等。當然有一些不同之處:在編譯時,您不能反映運行時實例,並且在運行時無法訪問在編譯期間刪除的方法,作用域和其他信息的內部結構。
這兩個API仍然是實驗性的,並可能在未來的某些部分發生變化,但它們非常有用,並且也有很好的文檔記錄。 Scala是一種多功能的語言,它們只比Java中的API複雜得多。
本文檔爲您帶來很遠:
http://www.scala-lang.org/api/2.11.7/scala-reflect/
http://www.scala-lang.org/api/2.11.7/scala-compiler/
http://docs.scala-lang.org/overviews/(頁面的底部)
這getClass[AnnotatedClass].getAnnotations
只給你Java註解,讓斯卡拉註解你有獲得Scala類型而不是隻有類。
它能夠在運行過程以及在編譯時訪問的反射,但有三種註釋:
平原批註僅在代碼:這些都可以從宏在訪問編譯單元,其中宏被調用,其中宏獲取訪問AST被過度的編譯單元共享
StaticAnnotations:這些可以通過階反射API被訪問
ClassfileAnnotations:這些代表存儲爲java註釋的註釋。如果你想通過Java Reflection API訪問它們,你必須在Java中定義它們。
下面是一個例子:
@SerialVersionUID(1) class Blub
現在,我們可以得到的註釋是這樣的:
import scala.reflect.runtime.universe._
val a = typeOf[Blub].typeSymbol.annotations.head
其實我們得到的是不是一個註釋的實例。運行時環境只是給了我們寫在字節碼中的東西:生成註釋的scala代碼。你可以打印出你的AST:現在
showRaw(a.tree)
,這已經是一個相當複雜的結構,但我們可以使用模式匹配分解它:
val Apply(_, List(AssignOrNamedArg(_,Literal(Constant(value))))) = a.tree
val uid = value.asInstanceOf[Long]
這是非常簡單的註釋OK (但是我們可以用Java編寫這些程序,並依靠JVM爲我們創建實例)。如果我們真的想評估該代碼並生成註釋類的實例,該怎麼辦? (對於@SerialVersionUID這不會幫助我們太多的類實際上並沒有給作爲訪問ID ...)我們也可以這樣做:
case class MyAnnotation(name: String) extends annotation.ClassfileAnnotation
@MyAnnotation(name = "asd")
class MyClass
object MyApp extends App {
import reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
val annotation = typeOf[MyClass].typeSymbol.annotations.head
val instance = toolbox.eval(toolbox.untypecheck(annotation.tree))
.asInstanceOf[MyAnnotation]
println(instance.name)
}
注意,這將調用,這需要編譯器很少的時間,特別是如果你第一次這樣做。精密的元編程應該在編譯時在Scala中完成。 Java中的很多東西只在運行時完成,因爲只能運行時元編程(嗯,有註釋處理器,但它們更有限)。
來源
2016-02-07 14:06:23
dth
如果你有6個問題,你應該問6個問題,而不是一個。特別是最後一個問題與其他問題完全無關。 –