2012-11-11 22 views
6

我正在編寫一個REST Web服務的包裝,我想要強類型化Scala API。是否有可能在Scala中使用帶有名稱和參數值的Map [String,Any]?

下面就是我到目前爲止做:

def getMentions(count: Option[Int] = None, 
       sinceID: Option[TweetID] = None, 
       maxID: Option[TweetID] = None, 
       trimUser: Option[Boolean] = None, 
       contributorDetails: Option[Boolean] = None, 
       includeEntities: Option[Boolean] = None) : List[Tweet] = { 
val parameters = Map("count" -> count, 
        "since_id" -> sinceID, 
        "max_id" -> maxID, 
        "trim_user" -> trimUser, 
        "contributor_details" -> contributorDetails, 
        "include_entities" -> includeEntities) 
/* 
* Convert parameters, which is a Map[String,Any] to a Map[String,String] 
* (Removing Nones) and pass it to an object in charge of generating the request. 
*/ 
... 
} 

這種方法的工作,但它需要我手動生成parameters地圖。 如果我能夠訪問代表參數及其值的地圖,我所做的將會更清晰。

回答

11

你可以用運行時反射來做到這一點,我相信你會得到答案,告訴你如何,如果你想這樣做,但這實際上是一個整潔的用例Scala 2.10's macros,所以這裏。首先,假設我們有一個名爲ParamMapMaker.scala文件:

object ParamMapMaker { 
    def paramMap: Map[String, Any] = macro paramMapImpl 

    def paramMapImpl(c: scala.reflect.macros.Context) = { 
    import c.universe._ 

    val params = c.enclosingMethod match { 
     case DefDef(_, _, _, ps :: Nil, _, _) => 
     ps.map(p => 
      reify((
      c.Expr[String](Literal(Constant(p.name.decoded))).splice, 
      c.Expr[Any](Ident(p.symbol)).splice 
     )).tree 
     ) 
     case _ => c.abort(c.enclosingPosition, "Can't call paramMap here!") 
    } 

    c.Expr[Map[String, Any]](Apply(Select(Ident("Map"), "apply"), params)) 
    } 
} 

我會離開蛇殼地圖鍵爲(容易)讀者練習。

我們也有一個測試文件(名爲Test.scala):

object Test extends App { 
    def foo(hello: String, answer: Int) = ParamMapMaker.paramMap 

    println(foo("world", 42)) 
} 

現在我們編譯這兩個:

scalac -language:experimental.macros ParamMapMaker.scala 
scalac Test.scala 

當我們運行Test我們會得到如下:

Map(hello -> world, answer -> 42) 

關於這件事的精妙之處在於沒有任何r的開銷沒有時間的反思。如果我們編譯測試文件,-Ymacro-debug-verbose,我們看到下面的代碼已經生成(實際上)爲foo身體在編譯時:

Map.apply[String, Any](
    scala.Tuple2.apply[String, String]("hello", hello), 
    scala.Tuple2.apply[String, Int]("answer", answer) 
) 

就如我們所期待。

+1

你知道是否有可能與sbt一起使用它? – mariosangiorgio

+3

我想通了。它只需要改變scala版本'scalaVersion:=「2.10.0-RC2」'並使用該特性在文件中導入' import language.experimental.macros' – mariosangiorgio

相關問題