JGSK - 18.类相关:Object - Scala & Kotlin篇

Table Of Contents

Scala 篇

Object

单例对象

在 Java 中实现单例对象通常需要自己实现一个类并创建 getInstance() 的方法然后在该方法里使用两次同步块或者使用更为优雅的基于 enum 的方式。而 Scala 中则更加简单,只要使用 object 声明就能创建一个单例对象。实际上之前我们创建的拥有 main() 方法的都是单例对象。

object Singleton {
  private var num = 0
  def sequence() = {
    num += 1
    num
  }
}

在方法的章节我们已经知道了在 Scala 中是没有静态成员这一说的,但是有时我们可能又真的需要有这种机制。Scala 就是采用了使用单例对象来实现类似的静态成员。

assert(1 == Singleton.sequence())
assert(2 == Singleton.sequence())
assert(3 == Singleton.sequence())

可以看到这种方式和在 Java 中调用静态方法一模一样,但是 Scala 中这其实是调用了一个对象的方法。

通常来说,Scala 中的单例对象一般用于提供常量及共享不可变对象。

伴生对象

我们知道使用 class 可以创建一个类,使用 object 可以创建一个单例对象,而类和单例对象是可以重名的,此时我们称该单例对象为此类的伴生对象。

伴生对象有以下特点

  • 伴生对象可以用于让一个类即拥有实例化方法又有静态方法。
  • 伴生对象可以用于创建同名类的实例。
  • 伴生对象必须与同名类位于同一源文件中。
  • 伴生对象与类可以互相访问各自的私有成员。

创建一个类

class Companion(private var balance: Int = 0) {
  val id = Companion.sequence()
}

创建该类的伴生对象

object Companion {
  private var num = 0

  def getInfo(account: Companion): String = {
    "Balance is " + account.balance
  }

  private def sequence(): Int = {
    num += 1
    num
  }

  def apply(initBalance: Int) = {
    new Companion(initBalance)
  }
}

使用类与伴生对象

//  通过伴生对象的 apply() 方法创建类的实例
val account = Companion(30)

//  在类中调用伴生对象的私有方法
assert(1 == account.id)

//  在伴生对象中访问类的私有属性
assert("Balance is 30" == Companion.getInfo(account))

继承

单例对象不能继承另一个单例对象,但是单例对象本身可以继承另一个类,从而扩展该类的功能,实现该类的可共享的默认对象

abstract class UndoableAction(val description: String) {
  def undo

  def redo
}

object DoNothing extends UndoableAction("Do nothing") {

  override def undo: Unit = {
    println("undo")
  }

  override def redo: Unit = {
    println("redo")
  }

}

以上 DoNothing 继承了 UndoableAction 类,可以用于作为 UndoableAction 的一个默认对象来使用。

应用程序对象

应用程序对象是继承类 App 的单例对象,该对象是一个应用程序的入口,可以通过 args 获得命令行参数。即使用了应用程序对象后你就不需要显示声明 main() 方法了,应用程序对象中定义的所有语句都会在 main() 方法中执行。

object Hello extends App {

  println("Hello World")

  if (args.length > 0) {
    println("The passed arguments are " + args)
  } else {
    println("There is no argument passed.")
  }
}

Kotlin 篇

Object

Kotlin 中的 Object 用法类似 Scala,但与 Scala 中的 Object 的最大区别是 Kotlin 不允许 Object 和类同名。

单例对象

object Singleton {
    private var num = 0

    fun sequence(): Int {
        num += 1
        return num
    }
}

同 Scala 一样,Kotlin 也通过 Object 来实现静态方法的功能。

assert(1 == Singleton.sequence())
assert(2 == Singleton.sequence())
assert(3 == Singleton.sequence())

伴生对象

伴生对象的概念与 Scala 基本一致,但是 Kotlin 的伴生对象是通过关键字 companion object 定义在类中,而不是类外,伴生对象不与类同名。

创建类和伴生对象

class Companion(private var balance: Int = 0) {
    //  Access private method of companion object
    val id = Companion.sequence()

    companion object Factory {
        private var num = 0

        fun getInfo(account: Companion): String {
            return "Balance is " + account.balance
        }

        fun create(initBalance: Int): Companion {
            return Companion(initBalance)
        }

        private fun sequence(): Int {
            num += 1
            return num
        }
    }
}

使用类与伴生对象

//  通过伴生对象的方法创建类的实例
val account = Companion.create(30)

//  在类中调用伴生对象的私有方法
assert(1 == account.id)

//  在伴生对象中访问类的私有属性
assert("Balance is 30" == Companion.getInfo(account))

object 表达式

object 表达式用于扩展类的功能,使用时类似 Java 中的匿名内部类。

定义一个类和接口

open class A(x: Int) {
    public open val y: Int = x
}

interface B {
    fun info()
}

使用 object 表达式扩展类和接口

val ab = object : A(1), B {
    override fun info() {
        println("info")
    }

    override val y: Int
        get() = 15
}
println(ab.y)
println(ab.javaClass)

以上示例本质上是生成了一个实现类和接口的匿名内部类的对象。在 Kotlin 中也可以不指定任何父类,直接产生匿名内部类:

val obj = object {
    val x = 10
    val y = 20
}
println(obj.x)
println(obj.javaClass)

总结

  • Scala 和 Kotlin 使用单例对象来提供其它语言中的全局常量和静态方法的特性
  • Scala 的伴生对象与类同名且平级,Kotlin 的伴生对象定义在类中且不同名
  • Kotlin 的 object 表达式可以实现简单的继承功能

项目源码见 JGSK/_18_object