2011-01-31 166 views
3

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

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

+0

通过避免调用新的B或新的C可以获得什么好处? – Monkey 2011-01-31 06:56:16

+0

我可能有许多子类(B,C,D,...),我想避免重复/样板代码。 – Adrian 2011-01-31 07:27:16

+0

你可以做你想做的唯一方法就是使用反射。正如我在答复中所述,这不是一个好主意。 – Monkey 2011-01-31 07:53:43

回答

1

您会将构造函数放在伴随对象中,而不是放在抽象类中。就像这样:

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

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

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

您可以使用反射来创建新实例。像这样的东西可以工作,但在我看来是不值得的麻烦。一方面你只能检查运行时是否存在合适的构造函数。

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 
0

看起来这是不可能的。根据斯卡拉书(Oderski,Spoon,Venners),您无法创建抽象类型为的实例。请参阅:抽象类型章节,货币案例研究。这可能会在稍后使用“虚拟课程”来支持。

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

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

我仍然好奇你为什么需要construct里面A。气味腥。

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

也许有更好的解决方案,但是这是迄今为止我发现了什么。