Scala特质函数:返回派生类型的实例

7
我有一个名为“Book”的特征,长这样:

trait Book{
 val sqlTableName;
 def getAll: Seq[ Book ] = { magicSQLFn( $"SELECT * FROM $sqlTableName" ) }
}

我有两个派生类型:

class Fiction extends Book{ val sqlTableName = "fiction" }
class NonFiction extends Book{ val sqlTableName = "nonfiction" );

当我在Fiction的实例上调用getAll时,我需要得到Seq[Fiction]。 我知道的一种方法是执行.map( _.asInstanceOf[ Fiction ] )。 但是,有没有一种方法可以避免这种情况?
实际上,我意识到更少错误的方法是能够为Fiction定义一个扩展Book的伴生对象,以便我可以在该对象上调用getAll(而不是在实例上进行)。但是,在这种情况下,我不确定如何将返回序列中的单个元素转换为Fiction类的实例,因为Fiction类将不再派生自Book。 我应该有两个不同命名的Book traits吗? 一个是这些对象的超级,另一个是这些类的超级类?
编辑:@Travis Brown的回复解决了我的初始问题。 如果有人对如何使用伴生对象而不是类实例处理此问题有评论,那就太好了!
1个回答

13
这更或多或少是F-边界多态的经典用例,它允许您在超类型的方法中引用特定子类型:
trait Book[B <: Book[B]] {
  val sqlTableName;
  def getAll: Seq[B] = { magicSQLFn( $"SELECT * FROM $sqlTableName" ) }
}

class Fiction extends Book[Fiction] { val sqlTableName = "fiction" }
class NonFiction extends Book[NonFiction] { val sqlTableName = "nonfiction" )

假设您的magicSQLFn将返回具有适当静态类型的内容,但它毕竟是魔法。F-边界多态性有其反对者,需要注意一些问题,但它是Scala和Java中广泛使用的模式。

谢谢!您对如何通过在伴生对象上调用getAll而不是在类实例上调用它来处理此问题是否有任何评论? - 0fnt
我不确定为什么在Fiction伴生对象上定义getAll意味着Fiction不能扩展Book?这可能值得跟进一下。 - Travis Brown
1
[B <: Book] 应该改为 [B <: Book[B]] - dips
1
能够做到这一点让我感到不舒服。但我很高兴我能解决我的问题。 - Ritwik Bose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接