这就是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方法返回的是实际使用的类型,而不是Abstract0。如果Concrete0有setOtherOption方法,那么(new Concrete0).setOption(1).setOtherOption(...)将起作用。
更新:回答JPP在评论中提出的后续问题(如何返回新实例):描述问题的一般方法是正确的(使用抽象类型)。然而,对于每个子类,创建新实例需要显式地进行。
一种方法是:
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)
}
另一种方法是遵循Scala集合库中使用的建造者模式。也就是说,setOption接收一个隐含的builder参数。这样做的优点是可以使用比'copy'更多的方法来构建新实例,并且可以进行复杂的构建。例如,setSpecialOption可以指定返回的实例必须是SpecialConcrete。
以下是解决方案的示例:
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的第二条评论。如果有多层嵌套,请使用类型参数而不是类型成员,并将Abstract0变为特征(trait):
UPDATE 2:
回复JPP的第二次评论。在存在多层嵌套的情况下,请使用类型参数代替类型成员,并将Abstract0改为一个trait:
trait Abstract0[+Self <: Abstract0[_]] {
}
class Concrete0 extends Abstract0[Concrete0] {
}
class RefinedConcrete0 extends Concrete0 with Abstract0[RefinedConcrete0] {
}
protected def self = this.asInstanceOf[Self]
,然后def setOption(...)= {... ; self }
,但这看起来有点丑... - Jean-Philippe Pellet