从基础抽象类构造子类

时间:2011-01-31 05:21:17

标签: scala abstract-class abstract-type

我想在抽象类中定义一个构造函数,它将创建具体的子类。

abstract class A {
  type Impl <: A
  def construct() : Impl = {
    val res = new Impl() //compile error: class type required but A.this.Impl found
    // do more initialization with res
  }
}

class B extends A {type Impl = B}
class C extends A {type Impl = C}
//...
val b = new B
b.construct() // this should create a new instance of B

这里有什么问题?这甚至可以实现吗? 编辑:澄清:我想抽象构造方法。我不想从子类或伴随对象中单独调用new Bnew C

6 个答案:

答案 0 :(得分:2)

如果要创建新实例,则需要显式调用构造函数。

abstract class A {

  def newInstance(): this.type;

  def construct() : this.type = {
    val res = newInstance()
  }
}

class B extends A {
  def newInstance() = new B()
}

Scala在运行时擦除类型,因此无法知道在创建类时Impl的含义。

答案 1 :(得分:1)

您可以将构造函数放在随播对象中,而不是在抽象类中。像这样:

object A {
  def apply(i:Int):A = new B(...)
  def apply(s:String):A = new C(...)
}

现在,您可以通过调用AA(42)来创建A("foobar")的实例。当然,字符串和整数参数仅是示例。如果所有构造函数的参数具有相同的类型,则此重载将不起作用。在这种情况下,您可以轻松创建不同的方法,并将其称为apply以外的其他方法。

答案 2 :(得分:1)

您可以使用反射来创建新实例。像这样的东西会起作用,但在我看来并不值得。首先,您只能检查运行时是否存在合适的构造函数。

def newInstance[T:ClassManifest]:T = {
  val klass = implicitly[ClassManifest[T]].erasure
  val constr = klass.getConstructors()(0)
  constr.newInstance().asInstanceOf[T]
}

abstract class A {
  def construct(implicit cm:ClassManifest[this.type]): this.type = {
    val res = newInstance[this.type]
    res
  }
}

class B extends A

答案 3 :(得分:0)

看起来这是不可能的。根据Scala的书(由Oderski,Spoon,Venners),您无法创建抽象类型的实例。请参阅:摘要类型章节,货币案例研究。稍后可以使用“虚拟类”来支持此功能。

答案 4 :(得分:0)

我提出以下模式:

abstract class A($params) {
  // do common initialisation here
  def construct() : A

  def foo(...) = {
    ...
    val bar = construct()
    ...
  }
}

class B($moreparams) extends A($someparams) {
  // do special initialisation here
  def construct() = new B()
}

您现在拥有的所有减少量恰好是每个子类一行。我认为这是一个很小的代价来支付a)一个工作解决方案,b)不使用反射(这基本上打破了静态类型系统为你提供的所有保证)。

我仍然很好奇你在construct内需要A的原因。闻起来很腥。

答案 5 :(得分:0)

Monkey回复后留下我的评论。解决此问题的一种方法是将Curiously Recurring Template Pattern(CRTP)与自我类型一起使用:

abstract class A[T <: A[T]] { this: T =>

  def newInstance(): T;

  def construct(): T = {
    val res = newInstance()
    res
  }

  def some(): T = this
}

class B extends A[B] {
  def newInstance() = new B()
}

也许有一个更好的解决方案,但这是我发现的目标。