伴生对象和伴生类

在前面的文章中,可能也有人注意到main方法并没有像java的static关键字,而且运行main方法的类定义的是object。object定义的类和class定义的类名字还可以一样。
比如class ScalaObject和object ScalaObject,我们可以说object ScalaObject是class ScalaObject的伴生对象,class ScalaObject是object ScalaObject的伴生类。伴生对象和伴生类可以相互访问私有的属性和方法。
在scala中定义伴生对象的属性和方法:

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中,是可以直接访问的,所以伴生对象和伴生类可以相互访问私有的属性和方法。

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是不行的,所以通过伴生对象来获取。运行后,可以看到两个地址是一样的。

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是可以省略的。

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方法。

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{

}

大军
847 声望183 粉丝

学而不思则罔,思而不学则殆


引用和评论

1 篇内容引用
0 条评论