Constructing subclasses from base abstract class

旧街凉风 提交于 2019-12-22 10:28:59

问题


I want to define a constructor in an abstract class that will create concrete subclasses.

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

What is wrong here? Is this even possible to implement? EDIT: Clarification: I want to abstract over the construct method. I do not want to call separately new B and new C from either subclasses or companion objects.


回答1:


You need to explicitly invoke a constructor if you want create a new instance.

abstract class A {

  def newInstance(): this.type;

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

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

Scala erases type at runtime so there is no way to know what Impl meant when the class was created.




回答2:


You would put the constructor in the companion object, not in the abstract class. Like this:

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

Now, you could create an instance of A by calling A(42), or A("foobar"), for example. The string and integer parameters are only examples, of course. If the parameters for all the constructors have the same types, this overloading will not work. In that case, you can easily create different methods and call them something other than apply.




回答3:


You can use reflection to create a new instance. Something like this would work but in my opinion is not worth the trouble. For one thing you would only be able to check if a suitable constructor existed at runtime.

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



回答4:


Looks like this is not possible. According to the Scala book (by Oderski, Spoon, Venners) you cannot create an instance of an abstract type. See: Abstract Types chapter, Currencies case study. This may be supported later with "virtual classes".




回答5:


I propose the following pattern:

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()
}

All reduandancy you have now is precisely one line per subclass. I consider this a small price to pay for a) a working solution that b) does not use reflection (which breaks essentially all guarantees a static type system offers you).

I am still curious why you would need construct inside A. Smells fishy.




回答6:


Following my comment left at Monkey response. One way how to solve this is to use the Curiously Recurring Template Pattern (CRTP) together with a self types:

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()
}

Perhaps there is a better solution, but this is so far what I found.



来源:https://stackoverflow.com/questions/4847869/constructing-subclasses-from-base-abstract-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!