单例对象和伴生对象

单例对象

在面向对象编程中一个常见的设计模式是定义一个只能被实例化一次的类。一个只能被实例化一次的类叫做“单例(singleton)”。

Scala不提供任何静态修饰符,这与构建纯面向对象语言的设计目标有关,在这种语言中,每个值都是一个对象,每个操作都是一个方法调用,每个变量都是某个对象的成员。拥有静态并不完全符合这个目标,与此同时,在代码中使用静态也有很多缺点。相反,Scala支持所谓的单例对象。单例对象允许将类的实例化限制为一个对象。可以使用单例对象来存储全局字段和实用函数或方法。

Scala类不能有静态变量和方法。相反,Scala类可以有一个单例对象或伴生对象。当只需要一个类的一个实例时,可以使用singleton对象。singleton也是一个Scala类,但是它只有一个实例。单例对象不能被实例化(对象创建)。单例对象中的函数和变量可以直接调用,而无需创建对象。

Scala 提供了关键字object 用于定义一个单例对象。下面的代码定义一个单例对象:

// 定义单例对象
object HelloWorld{

    def greet(){
        println("Hello World!")
        println("这是在单例对象中的输出内容。")
    }
}
    
// 调用
HelloWorld.greet

通常,main方法是在单例对象中创建的。因此,编译器在执行时不需要创建对象来调用main方法。例如,下面是带有main方法的一个Scala应用程序,可以直接编译运行:

object HelloScala {
   def main(args: Array[String]) {
       println("这是应用程序main方法里输出的内容。")
   }
}

伴生对象

与类同名的单例对象(object)称为伴生对象,类称为这个单例对象的伴生类。类和它的伴生对象必须定义在相同的包下和同一个源文件里。类和它的伴生对象可以互相访问其私有成员。

从Java程序员的角度,可以把单例对象当作是Java中可能会用到的静态方法工具类。(在Scala类中不能定义静态成员) 因为scala的语法结构中没有像java中的类,既可以拥有静态方法,也可以有非静态方法,所以就弄出这么一种结构,和该类class同名的object,并且该class与其同名的object必须要放在同一个.scala文件中,那么我们把这个object称之为同名class的伴生对象,把该class称之为同名object的伴生类。其实我们之前学习的数组Array、Map等的创建都用到了其对应的伴生对象的操作方式。

既可以使用类来创建对象,也可以用伴生对象来创建对象。二者唯一差别在于使用伴生类创建对象,必须要有new,而使用伴生对象来创建对象,直接省去new。在伴生类和伴生对象中可以互相访问对方的成员。

要想让伴生对象创建的对象也能访问伴生类中的公开成员,必须要在伴生对象中复写一个方法apply,返回值为该伴生类的对象。如果一个class有伴生的object对象,那么这个class有多少个构造函数,object就要定义多少个apply函数,apply参数列表与构造函数的参数列表相同。

// 伴生类
class Employee(var name: String, var age: Int) { // 主构造器

  private var married: Boolean = false

  // 辅助构造器1
  def this() = this("新员工", 0) 

  // 辅助构造器2
  def this(isMarried: Boolean) = {
    this() // 在辅助构造中的第一句话,调用了该类的辅助构造器1
    married = isMarried
  }

  def show() = println(s"姓名:$name, 年龄:$age, 婚否:$married")

}

// 伴生对象
object Employee{
  def apply():Employee = new Employee()
  
  def apply(isMarried: Boolean):Employee = new Employee(isMarried)
  
  def apply(name: String, age: Int):Employee = new Employee(name, age)
  
  def hello = println("Hello,大家好。这是一个静态方法")
}

object Demo2 {
  def main(args:Array[String]){
    val emp1 = Employee("张三", 23)
    emp1.show 
    Employee.hello
    
    val emp2 = Employee()
    emp2.show
    Employee.hello
    
    val emp3 = Employee(true)
    emp3.show
    Employee.hello
  }
}

《Spark原理深入与编程实战》