gpt4 book ai didi

scala - 如何使用 Scala 的 this 类型、抽象类型等来实现 Self 类型?

转载 作者:行者123 更新时间:2023-12-01 01:06:38 27 4
gpt4 key购买 nike

我无法在任何其他问题中找到答案。假设我有一个抽象父类(super class) Abstract0,它有两个子类 Concrete1 和 Concrete1。我希望能够在 Abstract0 中定义类似

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

其中 Self 将是具体的子类型。这将允许像这样对 setOption 进行链接调用:
val obj = new Concrete1.setOption(...).setOption(...)

并且仍然得到 Concrete1 作为 obj 的推断类型。

我不想要的是定义这个:
abstract class Abstract0[T <: Abstract0[T]]

因为它使客户更难处理这种类型。我尝试了各种可能性,包括抽象类型:
abstract class Abstract0 {
type Self <: Abstract0
}

class Concrete1 extends Abstract0 {
type Self = Concrete1
}

但是这样就不可能实现 setOption,因为 this在 Abstract0 中没有 Self 类型。并使用 this: Self =>在 Abstract0 中也不起作用。

这个问题有哪些解决方案?

最佳答案

这是什么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 接收一个隐式构建器参数。这样做的优点是可以使用更多方法来构建新实例,而不仅仅是“复制”,并且可以完成复杂的构建。例如。 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 Abstract0[+Self <: Abstract0[_]] {
// ...
}

class Concrete0 extends Abstract0[Concrete0] {
// ....
}

class RefinedConcrete0 extends Concrete0 with Abstract0[RefinedConcrete0] {
// ....
}

关于scala - 如何使用 Scala 的 this 类型、抽象类型等来实现 Self 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4313139/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com