返回介绍

数学基础

统计学习

深度学习

工具

Scala

二、单例对象

发布于 2023-07-17 23:38:23 字数 5378 浏览 0 评论 0 收藏 0

  1. Scala 的类不允许有static静态成员,为了提供类似Java 静态成员的功能,Scala 提供了单例对象singleton object

    单例对象是Scala 中的一等公民,其地位与class 相等,并不是其附属。定义单例对象并不会定义类型。

  2. 单例对象的定义看起来和类定义很像,只是class 关键字被 替换成了object 关键字。

    • 可以将单例对象视作存放 Java 中的静态字段和静态方法的地方。
    • 可以用单例对象名.成员名 的方式来访问单例对象的成员。
  3. 类和单例对象的区别:

    • 单例对象不接收参数,而类可以。因为无法采用new 实例化单例对象,因此没有任何手段来向它传参。
    • 每个单例对象都是通过一个静态变量引用合成类synthetic class 的实例来实现的(这个合成类的名字是对象名+美元符$),因此单例对象从初始化的语义上跟Java 静态成员是一致的:单例对象在有代码首次访问时才被初始化。
  4. Scala 在每个源码文件都隐式的引入了java.langscala 包的成员,以及名为Predef 的单例对象的所有成员。

    Predef 单例对象位于scala 包中,它包含许多有用的方法。

    • 如:在Scala 源码中使用println 时,实际调用了Predef.println 方法,而该方法实际调用的是Console.println 来执行具体操作。
    • 如:在Scala 源码中使用assert 时,实际调用了Predef.assert 方法。

2.1 伴生对象

  1. 当单例对象跟某个类共用同一个名字时,该单例对象被称作这个类的伴生对象companion object ,这个类被称作该单例对象的伴生类companion class

    • 必须在同一个源码文件中定义类和类的伴生对象。
    • 类和它的伴生对象可以相互访问对方的私有成员。
    
    
    xxxxxxxxxx
    class C{ // 伴生类 // 这里是类定义 } object C{ // 伴生对象 // 这里是单例对象的定义 }

2.2 孤立对象

  1. 没有伴生类的单例对象称作孤立对象standalone object。孤立对象有很多用途:作为工具方法的收集者、定义Scala 应用程序的入口等。

  2. 要运行一个Scala 程序,必须提供一个单例对象的名称,该单例对象必须包含一个main 方法,该方法必须接收一个Array[String] 作为参数,该方法的类型必须为Unit

    任何带有满足正确签名的main 方法的单例对象都能被用作应用程序的入口:

    
    
    xxxxxxxxxx
    object A{ def main(args:Array[String]) = { for(arg <- args) println(arg) } }
  3. scala 提供了一个特质scala.App 来便捷地定义main 方法:

    
    
    xxxxxxxxxx
    object A extends App{ for(arg <- args) println(arg) }

    首先在单例对象的名字后面添加extends App,然后将main 方法里的代码直接写在单例对象的花括号里。可以通过argsArray[String] 来访问命令行参数。

2.3 工厂对象

  1. 工厂对象包含了创建其它对象的方法。通常建议采用工厂对象的方法来构建对象,而不是直接采用 new 来构建对象,这些方法称作工厂方法。

    这种做法的优点是:对象创建的逻辑可以被集中管理,而对象是如何用具体的类来表示的细节可以隐藏起来。

    • 一方面,你的类库更容易被使用方所理解。
    • 另一方面,因为暴露的细节少,这样就提供了更多机会让你在未来不破坏使用方代码的前提下改变类库的实现。
  2. 工厂方法最直接的方案是创建类的伴生对象。

    
    
    xxxxxxxxxx
    abstract class C(name:String) { // 抽象父类 // 类的定义体 } class Child1(name:String,age:Int) extends C{ // 第一级子类 // 类的定义体 } class Child2(name:String,age:Int,job:String) extends Child1{ // 第二级子类 // 类的定义体 } ​ object C { def make_C(n:String,m:Int) : C = new Child1(n,m) def make_C(n:String,m:Int,j:String) : C = new Child2(n,m,j) }

    C 对象包含三个重载的make_C 工厂方法,每个工厂方法创建不同的类的对象(它们要么是C 的对象,要么是C 子类的对象)。

  3. 一旦有了工厂方法,就可以把目标类及其子类变成私有的,使得无法通过 new 来创建其对象。

    将类变成私有的方式之一是:将它们放在工厂对象中,并声明为私有的。

    
    
    xxxxxxxxxx
    abstract class C(name:String) { // 抽象父类 // 类的定义体 } ​ object C { private class Child1(name:String,age:Int) extends C{ // 第一级子类 // 类的定义体 } private class Child2(name:String,age:Int,job:String) extends Child1{ // 第二级子类 // 类的定义体 } def make_C(n:String,m:Int) : C = new Child1(n,m) def make_C(n:String,m:Int,j:String) : C = new Child2(n,m,j) }

    现在子类 Child1Child2 都无法在外部访问,但是可以通过单例对象C 的工厂方法访问。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文