鍍金池/ 問答/Scala/ 為什么第一個gender打印出來的是null

為什么第一個gender打印出來的是null

題目描述

為什么第一個gender打印出來的是null

題目來源及自己的思路

我已經(jīng)初試化了val gender: String = "male" ,但是貌似結(jié)果不對。

相關(guān)代碼

object ObjectExample {
  def main(args: Array[String]): Unit = {
    val student = new Student("zhu", 18, "zz")
  }
 
  class People(val name: String, val age: Int) {
    println("constructor")
    println(name)
    val gender: String = "male"
    println(gender)
    def this(name1: String) = {
      this("ci", 18)
      println("subconstructor")
      println(name)
    }
  }
 
  class Student(name: String, age: Int, val school: String) extends People(name, age){
    println("derived constructor")
    println(name)
    println(school)
    override val gender: String = "female"
    println(gender)
  }
}

錯誤信息

constructor
zhu
null
derived constructor
zhu
zz
female
回答
編輯回答
氕氘氚

把class移到外面仍然是無效的。
我對這個理解就是,val類型不能被初始化兩次。如果非要用重寫來“初試化”兩次,那么編譯器就會忽略父類的初始化,而去使用子類的初始化,而此時子類的初始化并沒有執(zhí)行,所以只能是null。改成lazy類型就可以了,當(dāng)然了,還有其他更多的方法。參考資料見Variable has been Initialized in Scala class, but what it print is null?(這個問題是我提出的)。scala官網(wǎng)也有解釋WHY IS MY ABSTRACT OR OVERRIDDEN VAL NULL?

2018年5月3日 04:33
編輯回答
別傷我

有本書適合這種類型的坑:《Scala謎題》

第4章 繼承
以下規(guī)則控制 val 的初始化和重載行為:
1.超類會在子類之前初始化;
2.按照聲明的順序?qū)Τ蓡T初始化;
3.當(dāng)一個 val 被重載時,只能初始化一次;
4.與抽象 val 類似,重載的 val 在超類構(gòu)造期間會有一個缺省的初始值。

里面也說了,可以使用 lazy val,但 lazy val 也有一些缺點:

1.由于在底層發(fā)生同步,這會引起輕微的性能成本;
2.不能聲明抽象 lazy val;
3.使用 lazy val 容易產(chǎn)生循環(huán)引用,從而導(dǎo)致首次訪問時發(fā)生棧溢出錯誤,甚至可能發(fā)生死鎖;
4.如果在對象間做了聲明而 lazy val 間的循環(huán)依賴卻不存時,就可能會發(fā)生死鎖,這種情況也許非常微妙,不易覺察。

此外,還有其余兩個方法,一個是將 val 改為 def,另一種是預(yù)初始化字段,如下:

  class Student(name: String, age: Int, val school: String) extends {
    override val gender: String = "female"
  } with People(name, age){
    println("derived constructor")
    println(name)
    println(school)
    println(gender)
  }
2018年5月5日 20:11