考虑一个简单的对象,它可以作为存储类型的一些内聚数据。我希望它有一个API:
我可以使用重载
轻松提供用于保存对象的APIobject CatsAndDogsStorage {
def save(key: String, cat: Cat): Future[Unit] = { /* write cat to db */ }
def save(key: String, dog: Dog): Future[Unit] = { /* save dog to Map */ }
/* other methods */
}
但是我找不到一种很好的方法来声明这样的方法来加载对象。理想情况下,我想要这样的事情:
// Futures of two unrelated objects
val catFuture: Future[Cat] = CatsAndDogsStorage.load[Cat]("Lucky")
val dogFuture = CatsAndDogsStorage.load[Dog]("Lucky")
我对Scala相当新,但我知道我有这些选项(按照最不喜欢的方式排序):
def loadCat(key: String): Future[Cat] = { /* ... */ }
def loadDog(key: String): Future[Dog] = { /* ... */ }
不是最简洁的方法。我不喜欢如果我决定将Cat重命名为其他东西,我也必须重命名该方法。
def load[T: ClassTag](key: String): Future[T] = classTag[T] match {
case t if t == classOf[Dog] => /* ... */
case c if c == classOf[Cat] => /* ... */
}
这个语句提供了所需的语法,但它在运行时失败,而不是编译时。
def load[T <: Cat](key: String): Future[Cat] = /* ... */
def load[T <: Dog](key: String)(implicit i1: DummyImplicit): Future[Dog]
如果您需要支持少数类型,此代码就变成了噩梦。它还使删除这些类型非常不方便
sealed trait Loadable
case class Cat() extends Loadable
case class Dog() extends Loadable
def load[T <: Loadable: ClassTag](key: String): Future[T] = classTag[T] match {
case t if t == classOf[Dog] => /* ... */
case c if c == classOf[Cat] => /* ... */
}
这具有2)的优点,同时防止用户除了狗或猫之外还要求任何东西。不过,我宁愿不改变对象层次结构。我可以使用union types缩短代码。
所以,最后一个解决方案没问题,但它仍然感觉很糟糕,也许还有另一种我无法弄清楚的已知方法。
答案 0 :(得分:2)
具有不同名称的函数执行类似的工作,但对于不同的类型对我来说似乎并不坏。
如果你真的想根据类型进行外观API调度,你可以使用类型类。
trait SaveFn[T] extends (T => Future[Unit]) {}
object SaveFn {
implicit object SaveDog extends SaveFn[Dog] { def apply(dog: Dog): Future[Unit] = ??? }
implicit object SaveCat extends SaveFn[Dog] { def apply(cat: Cat): Future[Unit] = ??? }
}
object Storage {
def save[T : SaveFn](in: T): Future[Unit] = implicitly[SaveFn[T]](in)
}
对于.load
案例:
trait LoadFn[T] extends (String => Future[T]) {}
object LoadFn {
implicit object LoadDog extends LoadFn[Dog] { def apply(key: String): Future[Dog] = ??? }
implicit object LoadCat extends LoadFn[Cat] { def apply(key: String): Future[Cat] = ??? }
}
object Storage {
def load[T : LoadFn](key: String): Future[T] = implicitly[LoadFn[T]](key)
}
对于.load
,无法根据.save
的参数找到推断,使用起来不太好:Storage.load[Dog]("dogKey")