如何使用Scala的这种打字,抽象types等来实现一个Selftypes?

在其他任何问题上我都找不到答案。 假设我有一个具有两个子类Concrete1和Concrete1的抽象超类Abstract0。 我想能够在Abstract0中定义类似的东西

def setOption(...): Self = {...} 

自我将成为具体的子types。 这将允许链接调用setOption像这样:

 val obj = new Concrete1.setOption(...).setOption(...) 

仍然得到Concrete1作为obj的推断types。

我不想要的是定义这个:

 abstract class Abstract0[T <: Abstract0[T]] 

因为这使得客户难以处理这种types。 我尝试了各种可能性,包括抽象types:

 abstract class Abstract0 { type Self <: Abstract0 } class Concrete1 extends Abstract0 { type Self = Concrete1 } 

但是那么就不可能实现setOption,因为Abstract0中没有typesSelf。 并且使用this: Self =>在Abstract0中也不起作用。

这个问题有什么解决scheme?

这是这个this.type的用途:

 scala> abstract class Abstract0 { | def setOption(j: Int): this.type | } defined class Abstract0 scala> class Concrete0 extends Abstract0 { | var i: Int = 0 | def setOption(j: Int) = {i = j; this} | } defined class Concrete0 scala> (new Concrete0).setOption(1).setOption(1) res72: Concrete0 = Concrete0@a50ea1 

正如你所看到的,setOption返回使用的实际types,而不是Abstract0。 如果Concrete0有setOtherOption那么(new Concrete0).setOption(1).setOtherOption(...)将工作

更新:在注释中回答JPP的后续问题(如何返回新实例:问题中描述的一般方法是正确的(使用抽象types),但是新实例的创build需要对每个子类都是显式的。

一种方法是:

 abstract class Abstract0 { type Self <: Abstract0 var i = 0 def copy(i: Int) : Self def setOption(j: Int): Self = copy(j) } class Concrete0(i: Int) extends Abstract0 { type Self = Concrete0 def copy(i: Int) = new Concrete0(i) } 

另一个是遵循斯卡拉collections库中使用的生成器模式。 也就是说,setOption会收到一个隐含的构build器参数。 这样做的好处是,构build新实例可以用更多的方法来完成,而不仅仅是“复制”,并且可以完成复杂的构build。 例如,setSpecialOption可以指定返回实例必须是SpecialConcrete。

这是解决scheme的一个例子:

 trait Abstract0Builder[To] { def setOption(j: Int) def result: To } trait CanBuildAbstract0[From, To] { def apply(from: From): Abstract0Builder[To] } abstract class Abstract0 { type Self <: Abstract0 def self = this.asInstanceOf[Self] def setOption[To <: Abstract0](j: Int)(implicit cbf: CanBuildAbstract0[Self, To]): To = { val builder = cbf(self) builder.setOption(j) builder.result } } class Concrete0(i: Int) extends Abstract0 { type Self = Concrete0 } object Concrete0 { implicit def cbf = new CanBuildAbstract0[Concrete0, Concrete0] { def apply(from: Concrete0) = new Abstract0Builder[Concrete0] { var i = 0 def setOption(j: Int) = i = j def result = new Concrete0(i) } } } object Main { def main(args: Array[String]) { val c = new Concrete0(0).setOption(1) println("c is " + c.getClass) } } 

更新2:回复JPP的第二个评论。 如果是多层嵌套,请使用types参数而不是types成员,并将Abstract0设置为特征:

 trait Abstract0[+Self <: Abstract0[_]] { // ... } class Concrete0 extends Abstract0[Concrete0] { // .... } class RefinedConcrete0 extends Concrete0 with Abstract0[RefinedConcrete0] { // .... } 

这是this.type的确切用例。 这将是:

 def setOption(...): this.type = { // Do stuff ... this }