我相信,你最好的選擇是將參數打包到不透明的Tuple
中,然後動態或靜態檢查,如果元組匹配Event
所需的參數。
假設下面的設置:
import scala.reflect._
sealed trait Event
case class EventA(a: Int, b: String) extends Event
object EventA {
// this val is needed only for the dynamic checking approach
final val tag = classTag[EventA]
}
case class EventB(p: String, q: Long, r: Long) extends Event
object EventB {
// this val is needed only for the dynamic checking approach
final val tag = classTag[EventB]
}
如果動態檢查,你只是比賽反對論據,並檢查它們是否與正確的長度和正確的元素類型的元組。這不是類型安全的,並且可以在運行時拋出ClassCastException
S,如果事件的參數有類型的類型參數(例如,l: List[Int]
或t: (Int, Double)
),這樣類型擦除踢
的代碼可以在不同的方式組織:
def make[T <: Event, Args](obj: Class[T], args: Args)(implicit tag: ClassTag[T]): T = ((tag, args) match {
case (EventA.tag, (a: Int, b: String)) => EventA(a, b)
case (EventB.tag, (p: String, q: Long, r: Long)) => EventB(p, q, r)
case (otherTag, otherArgs) => sys.error("wrong args for tag")
}).asInstanceOf[T]
scala> make(classOf[EventA], (10, "foo"))
res4: EventA = EventA(10,foo)
scala> make(classOf[EventB], ("bar", 10L, 20L))
res5: EventB = EventB(bar,10,20)
或者你也可以解壓縮到一個類能夠通過精確Event
類型爲類型參數:
class EventMaker[T <: Event] {
def apply[Args](args: Args)(implicit tag: ClassTag[T]): T = ((tag, args) match {
case (EventA.tag, (a: Int, b: String)) => EventA(a, b)
case (EventB.tag, (p: String, q: Long, r: Long)) => EventB(p, q, r)
case (otherTag, otherArgs) => sys.error("wrong args for tag")
}).asInstanceOf[T]
}
def make2[T <: Event] = new EventMaker[T]
scala> make2[EventA](10, "foo")
res6: EventA = EventA(10,foo)
scala> make2[EventB]("bar", 10L, 20L)
res7: EventB = EventB(bar,10,20)
也可以使用shapeless
庫(或使用手寫宏)靜態檢查參數。如果提供的參數不匹配case類所需的參數,這將導致編譯錯誤。此外,如果個別事件由案例分類表示,則此效果最佳,但也可以用於支持一些功能shapeless.ops.function.FnToProduct
import shapeless._
class EventMaker3[T <: Event] {
def apply[Args, H <: HList, H0 <: HList](args: Args)(implicit
genObj: Generic.Aux[T, H], // conversion between HList and case class
genArgs: Generic.Aux[Args, H0], // conversion between HList and arguments tuple
ev: H0 =:= H // assert that HList representations of the case class
// and provided arguments are the same
): T = genObj.from(genArgs.to(args))
}
def make3[T <: Event] = new EventMaker3[T]
scala> make3[EventA](10, "foo")
res8: EventA = EventA(10,foo)
scala> make3[EventB]("bar", 10L, 20L)
res9: EventB = EventB(bar,10,20)
scala> make3[EventA](1, 2)
<console>:21: error: Cannot prove that H0 =:= H.
make3[EventA](1, 2)
^