Scala - 伴生對象

伴生對象和伴生類

在前面的文章中,可能也有人注意到main方法並無像java的static關鍵字,並且運行main方法的類定義的是object。object定義的類和class定義的類名字還能夠同樣。
好比class ScalaObject和object ScalaObject,咱們能夠說object ScalaObject是class ScalaObject的伴生對象,class ScalaObject是object ScalaObject的伴生類。伴生對象和伴生類能夠相互訪問私有的屬性和方法。
在scala中定義伴生對象的屬性和方法:java

object ScalaObject2 {
  var str: String = "str"

  def fun(): Unit = {
    println("fun")
  }
}

編譯後會看到有ScalaObject2和ScalaObject2$兩個class文件。
ScalaObject2反編譯以下,這裏有3個靜態方法,一個是咱們上面定義的fun,另一個就是str的set和get方法。ScalaObject2.str就是調用get方法,ScalaObject2.str="str2"就是調用set方法。
在static裏能直接調用ScalaObject2..MODULE$,因此咱們知道ScalaObject2..MODULE$也是靜態的。
image.png
ScalaObject2$反編譯以下,從這裏能夠看出,他其實是一個單例對象。構造函數用private修飾,而後又用this指向MODULE$,讓MODULE$對外提供方法。
image.png
在下面的例子中,str是伴生類ScalaObject的私有屬性,在伴生對象ScalaObject中,是能夠直接訪問的,因此伴生對象和伴生類能夠相互訪問私有的屬性和方法。app

class ScalaObject {
  private var str: String = "str"
}

object ScalaObject {
  def main(args: Array[String]): Unit = {
    val scalaObject = new ScalaObject()
    println(scalaObject.str)
  }
}

單例

相似java的單例模式:
首先構造器默認private的,此時是不能直接經過構造器來建立這個對象,而後在伴生對象中,聲明一個方法用於建立這個對象,因爲伴生對象和伴生類能夠互相調用私有方法,因此是能夠經過構造器來建立這個對象。函數

object ScalaObject3 {
  private val scalaObject3: ScalaObject3 = new ScalaObject3

  def getInstance(): ScalaObject3 = {
    scalaObject3
  }
}

class ScalaObject3 private() {

}

而後在Test中建立一個單例,直接經過new是不行的,因此經過伴生對象來獲取。運行後,能夠看到兩個地址是同樣的。this

object Test {
  def main(args: Array[String]): Unit = {
    //new ScalaObject3()//erro
    val scalaObject1 = ScalaObject3.getInstance()
    val scalaObject2 = ScalaObject3.getInstance()
    println(scalaObject1)
    println(scalaObject2)
  }
}

apply

咱們在伴生對象中定義apply方法,而後直接類目+參數(有可能無參),實際上調用的是伴生對象的apply方法。
好比在下面例子中,直接調用ScalaObject4("hello")實際上調用的是ScalaObject4.apply方法,因此apply是能夠省略的。spa

object Test4 {
  def main(args: Array[String]): Unit = {
    ScalaObject4("hello")
    ScalaObject4.apply("hello")
  }
}

object ScalaObject4 {
  def apply(str: String): Unit = {
    println(str)
  }
}

另一個就是能夠省略到new關鍵字。下面例子中,ScalaObject5的構造器是私有的,因此直接new一個對象是不行的,ScalaObject5是調用apply方法才返回的ScalaObject5實例。
好比List(1,2,3),其實就是調用apply方法。scala

object Test5 {
  def main(args: Array[String]): Unit = {
    val scalaObject = ScalaObject5
    //val scalaObject1 = new ScalaObject5 erro
  }
}

object ScalaObject5 {
  def apply(): ScalaObject5 = {
    new ScalaObject5
  }
}
class ScalaObject5 private{

}