Skip to content

Commit b8adaed

Browse files
authored
Merge pull request #978 from som-snytt/review/singleton-objects
rewrote singleton objects tour
2 parents 48b55b2 + e028268 commit b8adaed

File tree

1 file changed

+67
-33
lines changed

1 file changed

+67
-33
lines changed

_tour/singleton-objects.md

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,67 +10,101 @@ num: 13
1010

1111
next-page: regular-expression-patterns
1212
previous-page: pattern-matching
13-
1413
redirect_from: "/tutorials/tour/singleton-objects.html"
14+
prerequisite-knowledge: classes, methods, private-methods, packages, option
1515
---
16+
An object is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val.
1617

17-
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`.
18+
As a top-level value, an object is a singleton.
1819

20+
As a member of an enclosing class or as a local value, it behaves exactly like a lazy val.
21+
# Defining a singleton object
22+
An object is a value. The definition of an object looks like a class, but uses the keyword `object`:
23+
```tut
24+
object Box
1925
```
20-
package test
2126

22-
object Blah {
23-
def sum(l: List[Int]): Int = l.sum
24-
}
27+
Here's an example of an object with a method:
2528
```
29+
package logging
2630
27-
This `sum` method is available globally, and can be referred to, or imported, as `test.Blah.sum`.
31+
object Logger {
32+
def info(message: String): Unit = println(s"INFO: $message")
33+
}
34+
```
35+
The method `info` can be imported from anywhere in the program. Creating utility methods like this is a common use case for singleton objects.
2836

29-
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.
37+
Let's see how to use `info` in another package:
3038

31-
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.
39+
```
40+
import logging.Logger.info
3241
33-
## Companions ##
42+
class Project(name: String, daysToComplete: Int)
3443
35-
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.
44+
class Test {
45+
val project1 = new Project("TPS Reports", 1)
46+
val project2 = new Project("Website redesign", 5)
47+
info("Created projects") // Prints "INFO: Created projects"
48+
}
49+
```
3650

37-
[Scaladoc]({{ site.baseurl }}/style/scaladoc.html) 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.
51+
The `info` method is visible because of the import statement, `import logging.Logger.info`.
3852

39-
A class and its companion object, if any, must be defined in the same source file. Like this:
53+
Imports require a "stable path" to the imported symbol, and an object is a stable path.
4054

41-
```tut
42-
class IntPair(val x: Int, val y: Int)
55+
Note: If an `object` is not top-level but is nested in another class or object, then the object is "path-dependent" like any other member. This means that given two kinds of beverages, `class Milk` and `class OrangeJuice`, a class member `object NutritionInfo` "depends" on the enclosing instance, either milk or orange juice. `milk.NutritionInfo` is entirely distinct from `oj.NutritionInfo`.
4356

44-
object IntPair {
45-
import math.Ordering
57+
## Companion objects
4658

47-
implicit def ipord: Ordering[IntPair] =
48-
Ordering.by(ip => (ip.x, ip.y))
49-
}
59+
An object with the same name as a class is called a _companion object_. Conversely, the class is the object's companion class. A companion class or object can access the private members of its companion. Use a companion object for methods and values which are not specific to instances of the companion class.
5060
```
61+
import scala.math._
5162
52-
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.
53-
54-
## Notes for Java programmers ##
63+
case class Circle(radius: Double) {
64+
import Circle._
65+
def area: Double = calculateArea(radius)
66+
}
5567
56-
`static` is not a keyword in Scala. Instead, all members that would be static, including classes, should go in a singleton object. They can be referred to with the same syntax, imported piecemeal or as a group, and so on.
68+
object Circle {
69+
private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
70+
}
5771
58-
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:
72+
val circle1 = new Circle(5.0)
5973
74+
circle1.area
6075
```
61-
class X {
62-
import X._
6376

64-
def blah = foo
77+
The `class Circle` has a member `area` which is specific to each instance, and the singleton `object Circle` has a method `calculateArea` which is available to every instance.
78+
79+
The companion object can also contain factory methods:
80+
```tut
81+
class Email(val username: String, val domainName: String)
82+
83+
object Email {
84+
def fromString(emailString: String): Option[Email] = {
85+
emailString.split('@') match {
86+
case Array(a, b) => Some(new Email(a, b))
87+
case _ => None
88+
}
89+
}
6590
}
6691
67-
object X {
68-
private def foo = 42
92+
val scalaCenterEmail = Email.fromString("[email protected]")
93+
scalaCenterEmail match {
94+
case Some(email) => println(
95+
s"""Registered an email
96+
|Username: ${email.username}
97+
|Domain name: ${email.domainName}
98+
""")
99+
case None => println("Error: could not parse email")
69100
}
70101
```
102+
The `object Email` contains a factory `fromString` which creates an `Email` instance from a String. We return it as an `Option[Email]` in case of parsing errors.
71103

72-
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]`.
104+
Note: If a class or object has a companion, both must be defined in the same file. To define companions in the REPL, either define them on the same line or enter `:paste` mode.
105+
106+
## Notes for Java programmers ##
73107

74-
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`.
108+
`static` members in Java are modeled as ordinary members of a companion object in Scala.
75109

76-
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.
110+
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)