斯卡拉:非法继承;自我类型Foo [T]不符合Foo [T]的自我类型T.

时间:2016-02-06 19:53:15

标签: scala self-type

我有以下代码段:

abstract class Foo[T <: Foo[T]] { self: T =>
  def bar(x: T): T
  def newFoo: Foo[T] = {
    new Foo[T] { self: T =>
      // ...
    }
 }

}

我需要在抽象类的方法中生成一个新的Foo实例。任何人都可以告诉我如何最好地接近这个?

谢谢, Hadil

2 个答案:

答案 0 :(得分:0)

自我类型self: T =>表示您的Foo[T]也必须是T。对于构成new Foo[T] { ... }的任意TT不是Foo[T]的实例。您也无法将自我类型添加到像new Foo[T] { ... }这样的匿名类,因为它没有意义。具体类在那一点上是或不是T

在自定义类型的情况下,以类型安全的方式构建类似def newFoo: Foo[T]的方法实际上是不可能的,因为您需要知道如何构造任意T。当每个T具有相同的构造函数时,您可以通过反射执行所需的操作。

import scala.reflect._

abstract class Foo[T <: Foo[T] : ClassTag] { self: T =>
  def bar(x: T): T
  def newFoo: Foo[T] = classTag[T].runtimeClass.newInstance.asInstanceOf[T]
}

class Bar extends Foo[Bar] {
  def bar(x: Bar): Bar = x
}

scala> val b = new Bar
b: Bar = Bar@2a2d45ba

scala> b.newFoo
res1: Foo[Bar] = Bar@146ba0ac

当存在构造函数参数时,这将停止工作:

case class Baz(i: Int) extends Foo[Baz] {
  def bar(x: Baz): Baz = x
}

scala> val baz = new Baz(0)
baz: Baz = Baz(0)

scala> baz.newFoo
java.lang.InstantiationException: Baz
  at java.lang.Class.newInstance(Class.java:427)
  at Foo.newFoo(<console>:16)

答案 1 :(得分:0)

好吧,你不知道将来Foo将被继承的具体类。特别是,您不知道此类将具有哪些构造函数参数以及如何为它们提供参数。如果你真的想要这样做,你必须做一些假设(你不能在编译时强制执行)来实现这一点。

所以你应该做的是,将此方法保留为抽象并在子类中实现它。如果这不是一个选项,那么您的整体课程设计可能会遇到一些问题。更好地呈现您想要建模并寻求帮助。

如果你假设,具体类的构造函数没有任何参数,你就可以实现newFoo

def newFoo = this.getClass.newInstance

不需要classTags或其他花哨的东西。