Constructing subclasses from base abstract class
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 wro开发者_C百科ng 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.
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.
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
.
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
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".
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.
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.
精彩评论