Skip to content

Commit 60df162

Browse files
committed
rewrote singleton objects tour
1 parent 0ccd732 commit 60df162

File tree

1 file changed

+79
-31
lines changed

1 file changed

+79
-31
lines changed

tutorials/tour/_posts/2017-02-13-singleton-objects.md

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,113 @@ num: 13
1010

1111
next-page: xml-processing
1212
previous-page: pattern-matching
13+
assumed-knowledge: classes, methods, private-methods, packages
1314
---
1415

15-
Methods and values that aren't associated with individual instances of a [class](classes.html) belong in *singleton objects*, denoted by using the keyword `object` instead of `class`.
16+
Singleton objects are like classes in that they contain methods and values, but there are a few key differences.
17+
* There is only one instance of a singleton object so their methods and values aren't associated with individual instances of a [class](classes.html).
18+
* Objects have no constructors so they cannot be instantiated
1619

20+
# Defining a singleton object
21+
Like with classes, the simplest form of an object is the keyword `object` and an identifier:
22+
```tut
23+
object Box
1724
```
18-
package test
1925

20-
object Blah {
21-
def sum(l: List[Int]): Int = l.sum
26+
Here's an example of an object with a method:
27+
```tut
28+
package logging
29+
30+
object Logger {
31+
def info(message: String): Unit = println(s"INFO: $message")
2232
}
2333
```
34+
The method `info` can be imported from anywhere in the program. Creating utility methods like this is a common use case for singleton objects (however, more sophisticated logging techniques exist). Let's see how to use `info` in another package:
2435

25-
This `sum` method is available globally, and can be referred to, or imported, as `test.Blah.sum`.
26-
27-
Singleton objects are sort of a shorthand for defining a single-use class, which can't directly be instantiated, and a `val` member at the point of definition of the `object`, with the same name. Indeed, like `val`s, singleton objects can be defined as members of a [trait](traits.html) or class, though this is atypical.
28-
29-
A singleton object can extend classes and traits. In fact, a [case class](case-classes.html) with no [type parameters](generic-classes.html) will by default create a singleton object of the same name, with a [`Function*`](http://www.scala-lang.org/api/current/scala/Function1.html) trait implemented.
36+
```tut
37+
import logging.Logger.info
3038
31-
## Companions ##
39+
class Project(name: String, daysToComplete: Int)
3240
33-
Most singleton objects do not stand alone, but instead are associated with a class of the same name. The “singleton object of the same name” of a case class, mentioned above, is an example of this. When this happens, the singleton object is called the *companion object* of the class, and the class is called the *companion class* of the object.
41+
val project1 = new Project("TPS Reports", 1)
42+
val project2 = new Project("Website redesign", 5)
43+
info("Created projects") // Prints "INFO: Created projects"
44+
```
3445

35-
[Scaladoc](https://wiki.scala-lang.org/display/SW/Introduction) has special support for jumping between a class and its companion: if the big “C” or “O” circle has its edge folded up at the bottom, you can click the circle to jump to the companion.
46+
The `info` method becomes visible in the scope of the package using `import logging.Logger.info`. You could also use `import logging.Logger._` to import everything from Logger.
3647

37-
A class and its companion object, if any, must be defined in the same source file. Like this:
48+
## Companion objects
3849

50+
A singleton object with the same name as a class is called a _companion object_. Conversely, the class is the object's companion class. The companion class and object can access each other's private members. Use a companion object for methods and values which are not specific to instances of the companion class.
3951
```tut
40-
class IntPair(val x: Int, val y: Int)
52+
import scala.math._
4153
42-
object IntPair {
43-
import math.Ordering
54+
class Circle(val radius: Double) {
55+
def area: Double = Circle.calculateArea(radius)
56+
}
4457
45-
implicit def ipord: Ordering[IntPair] =
46-
Ordering.by(ip => (ip.x, ip.y))
58+
object Circle {
59+
def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
4760
}
61+
62+
val circle1 = new Circle(5.0)
63+
64+
circle1.area
4865
```
4966

50-
It's common to see typeclass instances as [implicit values](implicit-parameters.html), such as `ipord` above, defined in the companion, when following the typeclass pattern. This is because the companion's members are included in the default implicit search for related values.
67+
The `class Circle` contains the val `radius` which is specific to each instance whereas the `object Circle` contains the method `calculateArea` which is the same for every instance.
5168

52-
## Notes for Java programmers ##
69+
A common pattern to simplify calls to the companion object's members from the companion class is to import everything:
70+
```tut
71+
import java.time.LocalDateTime;
72+
import java.time.DayOfWeek
5373
54-
`static` is not a keyword in Scala. Instead, all members that would be static, including classes, should go in a singleton object instead. They can be referred to with the same syntax, imported piecemeal or as a group, and so on.
74+
class Event(name: String, date: LocalDateTime) {
75+
import Event._
5576
56-
Frequently, Java programmers define static members, perhaps `private`, as implementation aids for their instance members. These move to the companion, too; a common pattern is to import the companion object's members in the class, like so:
77+
def isOnTheWeekend: Boolean = _isOnTheWeekend(date)
78+
}
5779
80+
object Event {
81+
private def _isOnTheWeekend(date: LocalDateTime) = Array(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY).contains(date.getDayOfWeek)
82+
}
83+
84+
val event = new Event("Dotty Days", LocalDateTime.of(2020, 2, 2, 9, 0, 0))
85+
event.isOnTheWeekend // true
5886
```
59-
class X {
60-
import X._
6187

62-
def blah = foo
63-
}
88+
The `class Event` imports `_isOnTheWeekend` from the `object Event`. To make a member inaccessible from outside, use `private[this]`.
89+
90+
Note: If a class or object has a companion, both must be defined in the same file. To define them in the REPL, you must enter `:paste` and then paste in the class and companion object code.
91+
92+
## Sharing global state
93+
94+
Objects can also be used to store global state (i.e. contain mutable fields), though this is not thread safe and should only be used in special cases.
6495

65-
object X {
66-
private def foo = 42
96+
```tut
97+
package ids
98+
99+
object UserIdGenerator {
100+
private var lastId = 0
101+
def newId: Int = {
102+
lastID += 1
103+
lastID
104+
}
67105
}
68106
```
107+
Because there is only one instance of UserIdGenerator, there is also only one instance of the `lastId` variable. Any call to `newId` from anywhere in the program will update `lastId`.
108+
```tut
109+
package users
110+
111+
import ids.UserIDGenerator.newId
69112
70-
This illustrates another feature: in the context of `private`, a class and its companion are friends. `object X` can access private members of `class X`, and vice versa. To make a member *really* private to one or the other, use `private[this]`.
113+
class User(id: Int)
71114
72-
For Java convenience, methods, including `var`s and `val`s, defined directly in a singleton object also have a static method defined in the companion class, called a *static forwarder*. Other members are accessible via the `X$.MODULE$` static field for `object X`.
115+
val user1 = new User(newId) // lastId gets updated to 1
116+
val user2 = new User(newId) // lastId gets updated to 2
117+
```
118+
119+
## Notes for Java programmers ##
73120

74-
If you move everything to a companion object and find that all you have left is a class you don't want to be able to instantiate, simply delete the class. Static forwarders will still be created.
121+
`static` is not a keyword in Scala. Instead, all members that would be static, including classes, should go in a singleton object instead.
122+
When using a companion object from Java code, the members will be defined in a companion class with a `static` modifier. This is called _static forwarding_. It occurs even if you haven't defined a companion class yourself.

0 commit comments

Comments
 (0)