Skip to content

Update singleton-objects.md to use scala 2/3 tabs #2497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 103 additions & 4 deletions _tour/singleton-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,52 @@ As a top-level value, an object is a singleton.
As a member of an enclosing class or as a local value, it behaves exactly like a lazy val.
# Defining a singleton object
An object is a value. The definition of an object looks like a class, but uses the keyword `object`:


{% tabs object-definition-box %}

{% tab 'Scala 2 and 3' for=object-definition-box %}
```scala mdoc
object Box
```
{% endtab %}

{% endtabs %}

Here's an example of an object with a method:
```
{% tabs singleton-logger-example class=tabs-scala-version %}

{% tab 'Scala 2' for=singleton-logger-example %}

```scala
package logging

object Logger {
def info(message: String): Unit = println(s"INFO: $message")
}
```
{% endtab %}

{% tab 'Scala 3' for=singleton-logger-example %}

```scala
package logging

object Logger:
def info(message: String): Unit = println(s"INFO: $message")
```
{% endtab %}

{% endtabs %}

The method `info` can be imported from anywhere in the program. Creating utility methods like this is a common use case for singleton objects.

Let's see how to use `info` in another package:
{% tabs singleton-usage-example class=tabs-scala-version %}

```
{% tab 'Scala 2' for=singleton-usage-example %}

```scala
import logging.Logger.info

class Project(name: String, daysToComplete: Int)
Expand All @@ -43,6 +72,24 @@ class Test {
info("Created projects") // Prints "INFO: Created projects"
}
```
{% endtab %}

{% tab 'Scala 3' for=singleton-usage-example %}

```scala
import logging.Logger.info

class Project(name: String, daysToComplete: Int)

class Test:
val project1 = Project("TPS Reports", 1)
val project2 = Project("Website redesign", 5)
info("Created projects") // Prints "INFO: Created projects"
```
{% endtab %}

{% endtabs %}


The `info` method is visible because of the import statement, `import logging.Logger.info`.

Expand All @@ -53,8 +100,11 @@ Note: If an `object` is not top-level but is nested in another class or object,
## Companion objects

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.
```
import scala.math._
{% tabs companion-object-circle class=tabs-scala-version %}

{% tab 'Scala 2' for=companion-object-circle %}
```scala
import scala.math.pow

case class Circle(radius: Double) {
import Circle._
Expand All @@ -69,10 +119,34 @@ val circle1 = Circle(5.0)

circle1.area
```
{% endtab %}

{% tab 'Scala 3' for=companion-object-circle %}
```scala
import scala.math.pow

case class Circle(radius: Double):
import Circle.*
def area: Double = calculateArea(radius)

object Circle:
private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)


val circle1 = Circle(5.0)

circle1.area
```
{% endtab %}

{% endtabs %}

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.

The companion object can also contain factory methods:
{% tabs companion-object-email class=tabs-scala-version %}

{% tab 'Scala 2' for=companion-object-email %}
```scala mdoc
class Email(val username: String, val domainName: String)

Expand All @@ -95,6 +169,31 @@ scalaCenterEmail match {
case None => println("Error: could not parse email")
}
```
{% endtab %}

{% tab 'Scala 3' for=companion-object-email %}
```scala
class Email(val username: String, val domainName: String)

object Email:
def fromString(emailString: String): Option[Email] =
emailString.split('@') match
case Array(a, b) => Some(Email(a, b))
case _ => None

val scalaCenterEmail = Email.fromString("[email protected]")
scalaCenterEmail match
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""".stripMargin)
case None => println("Error: could not parse email")
```
{% endtab %}

{% endtabs %}

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.

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.
Expand Down