@連城404:頗有趣,有可能把 @conf val port = 0 中的 "= 0" 去掉換成現實的類型說明嗎?即 @conf port: Int 這個賦值在此處徹底沒有實際意義,更像是實現限制而引入的噪聲。github
class Server { @conf val port = 0 // 上面的賦值閒得多餘, 能夠寫成: // @conf val port: Int }
確實沒有必要, 但意義仍是有的: IDE 不會報錯. (至少我用的IDEA 14 的 scala plugin 還不支持 Scala Macro)segmentfault
那 = 0
能夠去掉嗎?post
答案是能夠, 只是在被 @連城404 問到的時候我尚未想到辦法.scala
解決辦法方法其實不難, 前提是要對 Scala AST
很熟悉.設計
經查:code
case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree)
Modifiers
中有個 flags
屬性, 其中標記了val port
的各類特徵.對象
那麼 val port: Int
和 val port = 0
之間的區別則能夠從 flags
中體現出來.blog
class ModifierFlags { final val DEFERRED = 1 << 4 // was `abstract' for members | trait is virtual final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit ... }
ModifierFlags
裏定義了全部的 flags
值, 這裏摘錄其中兩個枚舉則對應上面兩種不一樣的寫法.字符串
具體實現的代碼的提交, 請見0475d92c.
如何發現這些細節 ?
print(x)
, 探查未知對象的展示信息, 多半可以提供重要線索;print(x.getClass)
, 上面的辦法無論用, 則須要查明它的類型, 而後去翻源碼了.
上面 v0.1.x
是一種基於ValDef
的聲明風格, 實際使用的時候會發現:
編寫簡單的同時, 代價是類名定義要與配置路徑要嚴格一致, 這並不是適合全部場景
好比寫個 Kafka
的消費客戶端, 要爲它設計其訪問服務的配置項
kafka_broker { host = 10.0.0.1 port = 12306 }
此時, 對應的類名就要是KafkaBroker
, 這顯然違背了設計意圖, 像 KafkaConsumer
纔是最爲天然的選擇.
與其大量時間糾結於命名上, 不如換個更好的聲明方式.
讓 @conf
帶參數能夠更改默認配置項路徑 :
class KafkaConsumer { @conf("kafka_broker.host") val host: String }
嗯, 不錯, 可以解決問題. 只是以爲像繞了一圈又回去了的感受:
class KafkaConsumer { val host = conf.getString("kafka_broker.host") }
對比這兩種寫法, 前者彷佛沒什麼優點了.
回顧用 Macro Annotation
解決配置項綁定的初衷, 是但願 將代碼的元信息與配置項創建映射關係, 減小因中間字符串的 Hard Code 爲重構帶來的額外成本.
爲此, 另外一種寫法誕生了, 它是受這篇 Adding Reflection to Scala Macros 的啓發.
其中的細節請見 v0.2.0
的 README, 就不累述於此了.
這種新寫法還有個好處, 就是可以方便經過從代碼來生成配置文件, 轉義的代價很小.
嗯...再寫個 sbt plugin
吧 :)