Scala:使用具体实例的返回类型实现方法
我需要一种在抽象类中强制执行方法的方法,以使其具有所调用对象的具体类的返回类型。最常见的例子是Scala:使用具体实例的返回类型实现方法,scala,abstract-type,self-type,Scala,Abstract Type,Self Type,我需要一种在抽象类中强制执行方法的方法,以使其具有所调用对象的具体类的返回类型。最常见的例子是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(new
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)
}
我可以这样做的事实导致,如果我复制的对象的唯一信息是它们属于a
的给定子类,那么:
// type error: Seq[A] is not a Seq[CA]!
def createCopies[CA <: A](seq: Seq[CA]): Seq[CA] = seq.map(_.copy(genNewId()))
//类型错误:Seq[A]不是Seq[CA]!
def createCopies[CA
但是,它们都没有真正强制实现返回自己的类型
但这不是很正常吗?否则,这意味着您不能仅仅通过示例扩展A
来添加一个新方法,因为它会自动破坏您试图创建的契约(即,新类的副本
不会返回该类的实例,而是A
)。
我觉得能够拥有一个完美的类a
,当你将它扩展为类B
时它就会崩溃,这一事实本身就是错误的。
但老实说,我很难用语言来描述它所造成的问题
更新:在仔细考虑之后,我认为如果类型检查(“返回类型==most派生类”)只在具体类中进行,而不是在抽象类或特征上进行,那么这可能是合理的。
不过,我不知道在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)
scala>def createCopies[CA val bs2:Seq[B]=createCopies(bs)
bs2:Seq[B]=列表(B@92334e4, B@6665696b)
不要强制在声明端绑定类型,除非您需要在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()))
def genNewId():Int=???
def createCopies[A1我能想到的唯一解决方案是:
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 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, "")
}
编辑
实际上我忘了删除copy方法。这可能更一般一些:
trait StrictSelf[T <: StrictSelf[T]] { self: T =>
type Self >: self.type <: T
}
abstract class A(id: Int) { self:StrictSelf[_] =>
def copy(newId:Int):Self
}
trait-StrictSelf[T
键入Self>:Self.type
def副本(newId:Int):Self
}
我认为在scala中不可能实现您想要的功能
如果我要:
class Base { type A }
class Other extends Base
class Sub extends Other
现在…我们希望类型A引用“子类的类型”
您可以从Base
的上下文中看到,(从编译器的角度)不太清楚具体的“子类类型”是什么表示,无论在父级中引用它的语法是什么。在Other
中,它表示Other
的实例,但在Sub中,它可能表示Sub
的实例。如果有两种方法返回A
,一种在Other
中实现,另一种在Sub
中实现,这是否意味着Base中定义的类型同时具有两种不同的含义/界限/限制?如果A
在这些类之外引用,会发生什么情况
我们拥有的最接近的东西是this.type
。我不确定理论上是否有可能放宽this.type
的含义(或者提供更宽松的版本),但在实现时,它意味着一个非常特定的类型,非常特定,唯一满足def foo:this.type
的返回值是this
本身
我很想按照你的建议去做,但我不确定它是如何工作的。让我们想象一下,this.type
意味着…更一般的东西。它会是什么?我们不能只说“this
的任何定义类型”,因为你不会想要带有MyTrait{type A=MyTrait}的类子类
是有效的。我们可以说“一个类型满足了这个
的所有类型”,但是当有人用另一个mixin写val a=new Foo时,它会让人感到困惑,我仍然不确定它的定义方式是否能够实现上面定义的Other
和Sub
我们试图混合静态和动态定义的类型
在Scala中,当你说class B{type T时,你已经指出了this.type
的情况。为什么它不适合你?this.type
是一个实例特定的类型(即两个不同A
实例的this.type
不相同),因此不适合我的copy()
method。我想提出类似的问题:如何约定定义类的任何子类都应该关注一些方法,因为final
新来者也可以阅读tpolat关于主题的文章。出于好奇,为什么不将其设为构造函数呢?每个子类都必须提供自己的副本
方法(也就是说,在具体类实现它之前,它将是一个抽象方法),因为只有具体的子类才知道对象“副本”的返回类型。在我看来,这不像是强制要求;事实上,Java是在Cloneable()中实现的,但以非类型安全的方式,并且如果该方法未被重写,则使用反射。CA#Self
不一定是CA
;因此,我无法访问复制实例中的CA
特定方法和字段。我看不到Cloneable
的示例是如何相关的,因为它确实不相关(也无法)强制子类重写Clone并返回inst
trait StrictSelf[T <: StrictSelf[T]] { self: T =>
type Self >: self.type <: T
}
abstract class A(id: Int) { self:StrictSelf[_] =>
def copy(newId:Int):Self
}
class Base { type A }
class Other extends Base
class Sub extends Other