1
1
---
2
2
layout : tour
3
- title : Singleton Objects
3
+ title : 单例对象
4
4
5
5
discourse : false
6
6
@@ -13,3 +13,100 @@ language: zh-cn
13
13
next-page : regular-expression-patterns
14
14
previous-page : pattern-matching
15
15
---
16
+
17
+ 单例对象是一种特殊的类, 有且只有一个实例. 和惰性变量一样, 单例对象是延迟创建的, 当它被引用到的时候创建.
18
+
19
+ 作为一个顶级值, 单例对象只有一个实例.
20
+
21
+ 作为一种包装类, 或者局部值, 单例对象表现得和惰性变量一样.
22
+
23
+ # 定义一个单例对象
24
+ 一个单例对象是就是一个值. 单例对象的定义方式很像类, 但是使用关键字 ` object ` :
25
+ ``` tut
26
+ object Box
27
+ ```
28
+
29
+ 下面例子中的单例对象包含一个方法:
30
+ ```
31
+ package logging
32
+
33
+ object Logger {
34
+ def info(message: String): Unit = println(s"INFO: $message")
35
+ }
36
+ ```
37
+ 方法 ` info ` 可以在程序中的任何地方被引用. 像这样创建功能性方法是单例对象的一种常见用法.
38
+
39
+ 下面让我们来看看如何在另外一个包中使用 ` info ` 方法:
40
+
41
+ ```
42
+ import logging.Logger.info
43
+
44
+ class Project(name: String, daysToComplete: Int)
45
+
46
+ class Test {
47
+ val project1 = new Project("TPS Reports", 1)
48
+ val project2 = new Project("Website redesign", 5)
49
+ info("Created projects") // Prints "INFO: Created projects"
50
+ }
51
+ ```
52
+
53
+ 因为 import 语句 ` import logging.Logger.info ` , 方法 ` info ` 在此处是可见的.
54
+
55
+ 想要使用引用, 被引用的标识需要一个"固定路径", 一个单例对象就是一个固定路径.
56
+
57
+ 注意: 如果一个 ` object ` 没定义在顶层而是定义在另一个类或者单例对象中, 那么这个单例对象和其他类普通成员一样是 "路径相关的". 这意味着有两种行为, ` class Milk ` 和 ` class OrangeJuice ` , 一个类成员 ` object NutritionInfo ` "依赖" 于包装它的实例, 要么是牛奶要么是橙汁. ` milk.NutritionInfo ` 则完全不同于` oj.NutritionInfo ` .
58
+
59
+ ## 伴生对象
60
+
61
+ 当一个单例对象和某个类共享一个名称时, 这个单例对象称为 _ 伴生对象_ . 同理, 这个类被称为是这个单例对象的伴生类. 类和它的伴生对象可以互相访问其私有成员. 使用伴生对象来定义那些在伴生类中不依赖于实例化对象而存在的成员变量或者方法.
62
+ ```
63
+ import scala.math._
64
+
65
+ case class Circle(radius: Double) {
66
+ import Circle._
67
+ def area: Double = calculateArea(radius)
68
+ }
69
+
70
+ object Circle {
71
+ private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
72
+ }
73
+
74
+ val circle1 = new Circle(5.0)
75
+
76
+ circle1.area
77
+ ```
78
+
79
+ 这里的 ` class Circle ` 有一个成员 ` area ` 是和具体的实例化对象相关的, 单例对象 ` object Circle ` 包含一个方法 ` calculateArea ` 则在每一个实例化对象中都是可见的.
80
+
81
+ 伴生对象也可以包含工厂方法:
82
+ ``` tut
83
+ class Email(val username: String, val domainName: String)
84
+
85
+ object Email {
86
+ def fromString(emailString: String): Option[Email] = {
87
+ emailString.split('@') match {
88
+ case Array(a, b) => Some(new Email(a, b))
89
+ case _ => None
90
+ }
91
+ }
92
+ }
93
+
94
+ val scalaCenterEmail = Email.fromString("[email protected] ")
95
+ scalaCenterEmail match {
96
+ case Some(email) => println(
97
+ s"""Registered an email
98
+ |Username: ${email.username}
99
+ |Domain name: ${email.domainName}
100
+ """)
101
+ case None => println("Error: could not parse email")
102
+ }
103
+ ```
104
+ 伴生对象 ` object Email ` 包含有一个工厂方法 ` fromString ` 用来根据一个 String 创建 ` Email ` 实例. 在这里我们返回的是 ` Option[Email] ` 以防有语法分析错误.
105
+
106
+ 注意: 类和它的伴生对象必须定义在同一个源文件里. 如果需要在 REPL 里定义类和其伴生对象, 需要将它们定义在同一行或者进入 ` :paste ` 模式.
107
+
108
+ ## Java 程序员的注意事项 ##
109
+
110
+ 在 Java 中 ` static ` 成员对应于 Scala 中的伴生对象的普通成员.
111
+
112
+ 在 Java 代码中调用伴生对象时, 伴生对象的成员会被定义成伴生类中的 ` static ` 成员. 这称为_静态转发_ . 这种行为发生在当你自己没有定义一个伴生类时.
0 commit comments