斯卡拉:混凝土实例的返回类型实现方法(Scala: implementing method with

2019-08-16 19:02发布

我需要一种方法来执行在抽象类中的方法有具体的类它被称为该对象的返回类型。 最常见的例子是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)
}

我已经看到了很多办法,包括在那些这个伟大的答案 。 然而,没有人真的强迫返回自己的类型实现。 例如,下面的类是有效的:

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 ,然后继续创建A2的具体子类。 但是,如果简化问题(因为它是用抽象类型的情况下),我不需要进一步扩大已经具体类。

Answer 1:

我能想到的唯一的解决办法是这样的一种:

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
}


Answer 2:

不要强行绑定在声明侧, 除非你需要的定义中绑定的类型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()))


Answer 3:

我不认为这是可能的斯卡拉做你想做的。

如果我是:

class Base { type A }
class Other extends Base
class Sub extends Other

现在......我们希望A型是指“子类的类型。”

你可以看到,从上下文Base ,它不是特别清楚(从编译器的角度来看),具体是什么“类型的子类”的意思,请不要介意什么语法是指它的父。 在Other那就意味着实例Other ,但在子这可能意味着实例Sub ? 难道是OK来定义方法的实现返回的OtherOther ,但不是在Sub ? 如果有两个方法返回A的,和一个在实现Other和其他在Sub ,是否意味着在基地具有两种不同的含义/界限/同时限制所定义的类型? 如果会发生什么A被称为这些类之外?

我们最接近的事是this.type 。 我不知道,如果它在理论上是可能的放松之意this.type (或提供宽松的版本),但实现它意味着一个非常具体的类型,因此具体的,唯一的返回值满足def foo:this.typethis本身。

我希望能够做你的建议是什么,但我不知道它是如何工作的。 让我们想象一下this.type意味着......更多的东西一般。 那会是什么? 我们不能只说“任何定义类型的this ,”因为你不希望class Subclass with MyTrait{type A=MyTrait}是有效的。 我们可以说“满足所有类型的类型this ,”但是,当有人写它变得混乱val a = new Foo with SomeOtherMixin ......,我仍不能确定它可能的方式,可以使一个被定义实施两者的OtherSub定义如上。

我们是排序的努力混合静态和动态定义的类型。

在Scala中,当你说class B { type T <: B } T是特定的实例, B是静态的(我使用的Java静态方法的意义这个词)。 你可以说class Foo(o:Object){type T = o.type}T将是每个实例不同....但,当你写type T=FooFoo是静态指定的类的类型。 你也可以同样有一个object Bar ,并提到了一些Bar.AnotherType 。 该AnotherType ,因为它本质上是“静”,(虽然不是真的叫Scala的“静态”),不参与继承中Foo



Answer 4:

然而,没有人真的强迫返回自己的类型实现。 例如,下面的类将是有效的。

但是,是不是正常的? 否则就意味着你不能仅仅延长A由例如添加新的方法,因为它会自动断开,你要创建的合同(即新类的copy不会回到这个类的一个实例,但的A )。 是能够张女士有一个完全正常类A ,一旦你作为类扩展它打破B觉得我错了。 但说实话我有它引起的问题,麻烦把话讲。

更新 :想更一下这个之后,我认为,如果类型检查(“返回类型==最派生类”)只在具体的类,从来没有抽象类或特性做出这可能是合理的。 我不知道的任何方式进行编码,在Scala的类型系统虽然。

我能做到这一点的事实引起,如果我做对象的副本,其中我的唯一信息是,他们是一个公司的一个子类给出的

为什么你不能只返回一个Seq[Ca#Self] ? 举例,这种变化通过列表BcreateCopies将如预期返回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: implementing method with return type of concrete instance