2017-02-05 20 views
0

特徵的構造我有這種情況Call類構造器之前在斯卡拉

trait D { 
    def someMethod(): Unit = {} 
    } 

    trait C { 
    val locations: Seq[Int] 

    someSomethingWithLocations() // calling this in constructor 

    def someSomethingWithLocations(): Unit = { 
     print(locations.length) 
    } 
    } 

    class B extends D with C { 
    override val locations: Seq[Int] = 1 :: 2 :: 3 :: Nil 
    someMethod() 
    } 

    def main(args: Array[String]): Unit = { 
    val b = new B 
    } 

當我運行這段代碼someSomethingWithLocations拋出空指針異常,因爲B類的構造函數沒有被調用,因此位置未初始化。 如果我改變B類的聲明

 class B extends{ 
     override val locations: Seq[Int] = 1 :: 2 :: 3 :: Nil 
     someMethod() 
     } with D with C 

編譯器抱怨的someMethod()未找到。我該如何解決這個問題?

現在我已經將位置聲明移到了不同​​的特徵上,我的程序按預期工作,但如果可能的話,我想避免不必要的特徵。

回答

3

您嘗試的解決方案叫做早期初始化程序。你可以不叫someMethod裏面,因爲:

  • 早期初始化只能包含val定義
  • 包含someMethod早期初始化後混合的特質D,所以不能用但

但無論如何,早期的初始化器應該被視爲固定初始化順序時的最後手段。回落到它之前,你應該先嚐試一些不太哈克解決方案:

  1. 而不是定義或特徵來推翻val,儘量使其成爲一個構造函數的參數。在調用任何構造函數代碼之前,構造函數參數0123'被初始化。在這裏,您可以通過引入一箇中間抽象類做到這一點:

    abstract class AbstractB(val locations: Seq[Int]) 
    class B extends AbstractB(1 :: 2 :: 3 :: Nil) with D with C { 
        someMethod() 
    } 
    
  2. 讓你val一個lazy val

    class B extends D with C { 
        override lazy val locations: Seq[Int] = 1 :: 2 :: 3 :: Nil 
        someMethod() 
    } 
    

    這樣locations不會在class B的構造函數,但只是在第一次訪問被初始化,在你的情況下,這將是trait C的構造函數(注意在這種情況下,lazy關鍵字使得該字段的初始化爲,而不是通常的而不是,這是關於lazy val s的常見直覺)。

    lazy val看起來像一個簡單和容易的解決方案,但我會建議先嚐試構造函數參數,如果可能的話。這是因爲lazy val本身可能會訪問另一個val,這可能尚未在此時初始化。通過這種方式,問題升級爲其他val,最後您可能會發現自己必須將其全部申報爲lazy

  3. 如果你仍然想使用早期的初始化,需要移出它的方法調用,並把它變成構造:

    class B extends { 
        override val locations: Seq[Int] = 1 :: 2 :: 3 :: Nil 
    } with D with C { 
        someMethod() 
    }