2011-01-31 135 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() 
} 

也許有更好的解決方案,但是這是迄今爲止我發現了什麼。