2016-02-07 52 views
2

scala似乎是JVM世界的一個很好的補充。它讓我想起了嵌套在JVM世界中的C++,C#和Swift的一個奇怪的混合體。從版本2.11開始,scala反射功能的(當前)狀態是什麼,特別是wrt註釋?

但是,由於缺少或過時的文檔,許多scala的功能可能無法訪問。

這在反射能力方面看起來尤其如此。

例如,我正在評估是否有可能使用scala註釋在運行時或編譯時增加scala類。我正在使用最新的Scala版本2.11。作爲一個激勵的例子,假設我做了一個case class SimpleAnnotation() extends StaticAnnotation。我希望在運行時找到帶有該註釋的所有case class

這可能是註釋中最典型的和香草的用例。

在C#和Java中,在運行時確定給定的類是否被註釋是相對直接的。這是一個典型的用例,帶有規範的答案。然而在斯卡拉,我不清楚自己應該做什麼來實現這種行爲,甚至是否有可能。特別是,在掃描一些以前的材料scala註釋和反射後,我仍然想知道:

  • 這可能嗎?
  • 這是唯一可能在運行時或complile時間?
  • 這是唯一可能之前或之後scala版本2.10?
  • 這是唯一可能的使用Java註釋scala類?
  • 爲什麼getClass[AnnotatedClass].getAnnotations會返回這樣看似混亂的信息?
  • 爲什麼宏和反射似乎在scala中混合?

任何指導意見......我肯定我不是唯一一個感到困惑的人。

+1

如果你有6個問題,你應該問6個問題,而不是一個。特別是最後一個問題與其他問題完全無關。 –

回答

5

反射和宏共享很多的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類型而不是隻有類。

它能夠在運行過程以及在編譯時訪問的反射,但有三種註釋:

  1. 平原批註僅在代碼:這些都可以從宏在訪問編譯單元,其中宏被調用,其中宏獲取訪問AST被過度的編譯單元共享

  2. StaticAnnotations:這些可以通過階反射API被訪問

  3. 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中的很多東西只在運行時完成,因爲只能運行時元編程(嗯,有註釋處理器,但它們更有限)。

+0

感謝您對明顯含糊問題的詳細回答。 –

相關問題