一個簡單的解決方案,以避免調用Builder.build1
當定義一個隱式轉換到自動包裝任何值轉換成Some
處理選項:
implicit def wrap[T](x: T) = Some(x)
和吊杆,可以省略包裝並直接做:
scala> Builder.build1(a = 123, c = 456)
res1: Map[String,Int] = Map(a -> 123, c -> 456)
但是,這是非常危險的,因爲選項是普遍的,你不想拉這樣一個普通的進入範圍。 爲了解決這個問題,你可以定義自己的「選項」類,你只使用定義這些可選參數的目的:
abstract sealed class OptionalArg[+T] {
def toOption: Option[T]
}
object OptionalArg{
implicit def autoWrap[T](value: T ): OptionalArg[T] = SomeArg(value)
implicit def toOption[T](arg: OptionalArg[T]): Option[T] = arg.toOption
}
case class SomeArg[+T](value: T) extends OptionalArg[T] {
def toOption = Some(value)
}
case object NoArg extends OptionalArg[Nothing] {
val toOption = None
}
然後,您可以重新定義Build.build1
爲:
def build1(a: OptionalArg[Int] = NoArg, b: OptionalArg[Int] = NoArg, c: OptionalArg[Int] = NoArg): Map[String, Int]
而且然後再次,你可以直接調用Build.build1
而不Some
明確地包裹的說法:
scala> Builder.build1(a = 123, c = 456)
res1: Map[String,Int] = Map(a -> 123, c -> 456)
由於顯着的差異,現在我們不再需要進行危險廣泛的轉換來應對了。
UPDATE:在回答下面「的評論在我需要走得更遠,ARG可以是單個數值或者列表,我有可怕的一些(名單(某事))在我的客戶今天代碼」
您可以添加另一個轉換到各個參數包裝成一個元素列表:
implicit def autoWrapAsList[T](value: T ): OptionalArg[List[T]] = SomeArg(List(value))
然後說你的方法需要這樣的可選列表:
def build1(a: OptionalArg[List[Int]] = NoArg, b: OptionalArg[Int] = NoArg, c: OptionalArg[Int] = NoArg): Map[String, Int] = {
val optPairs = List(a.map("a" -> _.sum),
b.map("b" -> _),
c.map("c" -> _))
val pairs = optPairs.flatten
Map(pairs: _*)
}
您可以現在要麼通過一個單獨的元素或列表(或像以前一樣,沒有參數的話):
scala> Builder.build1(a = 123, c = 456)
res6: Map[String,Int] = Map(a -> 123, c -> 456)
scala> Builder.build1(a = List(1,2,3), c = 456)
res7: Map[String,Int] = Map(a -> 6, c -> 456)
scala> Builder.build1(c = 456)
res8: Map[String,Int] = Map(c -> 456)
最後一個警告:儘管我們已經定義了自己的「選項「類,但仍應該始終謹慎使用隱式轉換,因此需要一些時間來平衡便利是否值得您的用例中的風險。
不錯的把戲!儘管如此,我發現構建器的方法更強大(在不同的客戶端方法中重用部分映射) –
的確如此,但它也是更多的樣板文件,所以如果有定義的話,通常只會使用這個路徑。我上面的解決方案可能並不總是合適的,但應用起來要少得多。 –
在我的需求中更進一步,arg可以是單個值或列表,並且我今天在我的客戶端代碼中有一些(List(sth))可怕的。 –