我需要一种方法来强制抽象类中的方法,使其具有返回类型的被调用对象的具体类。最常见的示例是copy()
方法,我目前正在使用基于抽象类型的方法:
abstract class A(id: Int) {
type Self <: A
def copy(newId: Int): Self
}
class B(id: Int, x: String) extends A(id) {
type Self = B
def copy(newId: Int) = new B(newId, x)
}
class C(id: Int, y: String, z: String) extends A(id) {
type Self = C
def copy(newId: Int) = new C(newId, y, z)
}
我已经看到了许多方法,包括this great answer中的方法。但是,它们都没有真正强制实现返回自己的类型。例如,以下类是有效的:
class D(id: Int, w: String) extends A(id) {
type Self = A
def copy(newId: Int) = new D(newId, w) // returns an A
}
class E(id: Int, v: String) extends A(id) {
type Self = B
def copy(newId: Int) = new B(newId, "")
}
我可以这样做的事实导致,如果我正在复制对象的副本,我所拥有的唯一信息是它们属于A
的给定子类:
// type error: Seq[A] is not a Seq[CA]!
def createCopies[CA <: A](seq: Seq[CA]): Seq[CA] = seq.map(_.copy(genNewId()))
我能做到更好,更安全的方式吗?
编辑:如果可能的话,我希望能够创建任意深层次的抽象类层次结构。也就是说,在前面的示例中,我希望能够创建扩展A2
的抽象类A
,然后继续创建{{1具体的子类。但是,如果这简化了问题(因为它是抽象类型的情况),我不需要进一步扩展已经具体的类。
答案 0 :(得分:24)
我能想到的唯一解决方案就是这个:
trait CanCopy[T <: CanCopy[T]] { self: T =>
type Self >: self.type <: T
def copy(newId: Int): Self
}
abstract class A(id: Int) { self:CanCopy[_] =>
def copy(newId: Int): Self
}
以下编译:
class B(id: Int, x: String) extends A(id) with CanCopy[B] {
type Self = B
def copy(newId: Int) = new B(newId, x)
}
class C(id: Int, y: String, z: String) extends A(id) with CanCopy[C] {
type Self = C
def copy(newId: Int) = new C(newId, y, z)
}
以下内容无法编译:
class D(id: Int, w: String) extends A(id) with CanCopy[D] {
type Self = A
def copy(newId: Int) = new D(newId, w) // returns an A
}
class E(id: Int, v: String) extends A(id) with CanCopy[E] {
type Self = B
def copy(newId: Int) = new B(newId, "")
}
我实际上忘了删除复制方法。这可能更通用:
trait StrictSelf[T <: StrictSelf[T]] { self: T =>
type Self >: self.type <: T
}
abstract class A(id: Int) { self:StrictSelf[_] =>
def copy(newId:Int):Self
}
答案 1 :(得分:4)
除非在A
itelf 的定义中需要绑定,否则不要强制声明方上绑定的类型。以下就足够了:
abstract class A(id: Int) {
type Self
def copy(newId: Int): Self
}
现在强制使用网站上的Self
类型:
def genNewId(): Int = ???
def createCopies[A1 <: A { type Self = A1 }](seq: Seq[A1]): Seq[A1] =
seq.map(_.copy(genNewId()))
答案 2 :(得分:1)
我认为scala不可能做你想做的事。
如果我要:
class Base { type A }
class Other extends Base
class Sub extends Other
现在......我们希望类型A引用“子类的类型。”
你可以从Base
的上下文中看到,从编译器的角度来看,特定的“子类类型”的含义并不是特别清楚,从不明白在语法中引用它的语法是什么。家长。在Other
中,它意味着Other
的实例,但在Sub中,它可能意味着Sub
的实例?是否可以定义方法的实现,在Other
而不是Other
中返回Sub
?如果有两个方法返回A
,一个在Other
中实现,另一个在Sub
中实现,这是否意味着Base中定义的类型有两个不同的含义/边界/限制同时?那么如果在这些类之外引用A
会发生什么?
我们最接近的是this.type
。我不确定理论上是否可以放宽this.type
的含义(或提供更宽松的版本),但实现它意味着一个非常特定的类型,所以特定的唯一返回值满足{{ 1}}本身就是def foo:this.type
。
我希望能够做你的建议,但我不确定它是如何工作的。让我们假设this
意味着......更为一般的东西。会是什么?我们不能只说“任何已定义的this.type
类型”,因为您不希望this
有效。我们可以说“满足所有class Subclass with MyTrait{type A=MyTrait}
类型的类型”,但当有人写this
时会让人感到困惑......我仍然不确定它是否会以某种方式定义启用上面定义的val a = new Foo with SomeOtherMixin
和Other
的实现。
我们试图混合使用静态和动态定义的类型。
在Scala中,当您说Sub
时,class B { type T <: B }
特定于实例,而T
是静态的(我在java中使用静态方法时使用的那个词) 。您可以说B
,class Foo(o:Object){type T = o.type}
对于每个实例都不同....但是当您编写T
时,type T=Foo
是该类的静态指定类型。您也可以拥有Foo
,并提到了一些object Bar
。 Bar.AnotherType
,因为它基本上是“静态的”(虽然在Scala中并不真正称为“静态”),但它不参与AnotherType
中的继承。
答案 3 :(得分:0)
但是,它们都没有真正强制实现返回自己的类型。例如,以下类是有效的。
但这不正常吗?否则,这意味着您不能仅仅通过示例扩展A
来添加新方法,因为它会自动破坏您尝试创建的合同(也就是说,新类copy
不会返回此类的实例,但是A
)。
能够拥有一个非常精细的课程A
的事实,一旦你将其作为课程B
延长就会中断,这对我来说是错误的。
但说实话,我无法对其引起的问题说出来。
UPDATE :在考虑了一下这个之后,我认为如果类型检查(“返回类型==最多派生类”)仅在具体类中进行并且从不在抽象类或特征上。 我不知道有任何方法可以在scala类型系统中对其进行编码。
我可以这样做的事实导致,如果我正在复制对象的副本,我所拥有的唯一信息是它们是A的给定子类
为什么你不能只返回Seq[Ca#Self]
?例如,通过此更改,B
到createCopies
的列表会按预期返回Seq[B]
(而不仅仅是Seq[A]
:
scala> def createCopies[CA <: A](seq: Seq[CA]): Seq[CA#Self] = seq.map(_.copy(123))
createCopies: [CA <: A](seq: Seq[CA])Seq[CA#Self]
scala> val bs = List[B]( new B(1, "one"), new B(2, "two"))
bs: List[B] = List(B@29b9ab6c, B@5ca554da)
scala> val bs2: Seq[B] = createCopies(bs)
bs2: Seq[B] = List(B@92334e4, B@6665696b)