【发布时间】:2016-05-02 18:08:00
【问题描述】:
考虑一个简单的对象,它可以存储一些按类型区分的内聚数据。我希望它有一个 API:
- 一致且简洁;
- 编译时安全。
我可以通过使用重载轻松提供这样的API来保存对象:
object 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 还很陌生,但我知道我有这些选项(从最不喜欢的开始排序):
1。不同的方法名
def loadCat(key: String): Future[Cat] = { /* ... */ }
def loadDog(key: String): Future[Dog] = { /* ... */ }
不是最简洁的方法。我不喜欢如果我决定将 Cat 重命名为其他名称,我也必须重命名该方法。
2。提供的类的运行时检查
def load[T: ClassTag](key: String): Future[T] = classTag[T] match {
case t if t == classOf[Dog] => /* ... */
case c if c == classOf[Cat] => /* ... */
}
这个给出了所需的语法,但它在运行时失败,而不是编译时。
3。虚拟隐含
def load[T <: Cat](key: String): Future[Cat] = /* ... */
def load[T <: Dog](key: String)(implicit i1: DummyImplicit): Future[Dog]
当您需要支持多种类型时,此代码将成为噩梦。删除这些类型也很不方便
4。密封特征 + 运行时检查
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 来缩短代码。
所以,最后一个解决方案是可以的,但它仍然感觉 hack-ish,也许还有另一种我无法弄清楚的已知方法。
【问题讨论】:
-
我认为你应该检查Type Classes,这是类型驱动泛型专业化的常用模式。
标签: scala api-design