Mike Slinn

Objects

— Draft —

Published 2014-01-02. Last modified 2016-03-02.
Time to read: 2 minutes.

Singletons and companion objects are explored in this lecture.

The sample code for this lecture can be found in courseNotes/src/main/scala/ClassWithCompanion.scala.

Singletons

Singletons provide an equivalent to static methods as found in Java, C++ and C#. However, singletons go beyond this.

  • A singleton can inherit methods from other classes and/or traits, which cannot be done with Java static methods.
  • A singleton can be passed as a parameter, perhaps via an inherited interface.
  • A singleton can exist within the scope of a surrounding class or method, just as Java can have inner classes.

Scala has the reserved word object which is used to define a singleton object.

Let’s see an object in action. To do this, we’ll start the sbt console, and import all of the classes and traits defined in the animals package, including Frog5.

$ sbt console
[info] Loading global plugins from /home/mslinn/.sbt/0.13/plugins
[info] Done updating.
[info] Loading project definition from /var/work/course_scala_intro_code/courseNotes/project
[info] Done updating.
[info] Set current project to IntroScalaCourse (in build file:/var/work/course_scala_intro_code/courseNotes/)
[info] Starting scala interpreter...

scala> import animals._
import animals._

Now we can define an object called OnlyOne. Objects are often declared with the first letter in upper case, but exceptions are common.

Scala REPL
scala> object OnlyOne extends Frog5(canSwim=true, numLegs=2, breathesAir=true)
defined object OnlyOne
scala> %}def doSomething(frog: Frog5): Unit = println(frog) doSomething: (frog: Animals.Frog5)Unit
scala> %}doSomething(OnlyOne) canSwim: true; 2 legs; breathesAir: true

Companion Objects and the REPL

Companion objects are singletons associated with a class in which you can define fields, methods, inner objects, inner classes and inner traits. In order for companion objects to have their magic activated, they must have the same name as the class they are associated with, and must be defined in the same file as a class of the same name.

The REPL’s paste mode was designed to allow companion objects to be defined with companion classes. For example, let’s redefine Frog5 as Frog6 and give it a companion object.

Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
scala> class Frog6(val canSwim: Boolean, numLegs: Int, breathesAir: Boolean) { override def toString = s"canSwim: $canSwim; $numLegs legs; breathesAir: $breathesAir" }
object Frog6 { def apply(canSwim: Boolean=true, numLegs: Int=4, breathesAir: Boolean=true) = new Frog6(canSwim, numLegs, breathesAir) } ^D // Exiting paste mode, now interpreting.
defined class Frog6 defined object Frog6

If both of these definitions were placed in the same file then Frog6 would be a companion object of the Frog6 class, and the Frog6 class would be a companion class to the Frog6 object. In Scala, methods called apply are default methods, and are often used as factories. Because they are default methods, you don’t have to use their name when invoking them.

Notice that the parameters for the companion object’s apply method are the same as the parameters to the primary constructor for the class. This is very common.

For example, you could create a new Frog6 instance with either of the following statements; both are equivalent.

Scala REPL
scala> val frog6a = Frog6(canSwim=true)
frog6a: Frog6 =  canSwim: true; 4 legs; breathesAir: true
scala> %}val frog6b = Frog6.apply(canSwim=true) frog6b: Frog6 = canSwim: true; 4 legs; breathesAir: true

Notice that the apply method that I defined has default values for all parameters, so the default values of numLegs and breathesAir were used because those parameters were not specified. Also notice that when a companion object is defined, its name (Frog6) refers to the singleton instance.

BTW, Ammonite (discussed in the SBT Global Setup and SBT Project Setup lectures) has a block input mode, which is similar to the Scala REPL’s :paste mode.

Importing Methods and Properties

If you want the methods and properties defined in the companion object to be available in the companion class, you must fully qualify them or import them.

Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
scala> class Frog7(val canSwim: Boolean, numLegs: Int, breathesAir: Boolean) { import Frog7.croak
def makeNoise = croak(3)
override def toString = s"canSwim: $canSwim; $numLegs legs; breathesAir: $breathesAir" }
object Frog7 { def apply(canSwim: Boolean=true, numLegs: Int=4, breathesAir: Boolean=true) = new Frog7(canSwim, numLegs, breathesAir)
def croak(times: Int): String = ("Croak " * times).trim } // Entering paste mode (ctrl-D to finish)
// Exiting paste mode, now interpreting.
defined class Frog7 defined module Frog7
scala>
Frog7(canSwim=true, 4, breathesAir=true).makeNoise // Entering paste mode (ctrl-D to finish)
res0: String = "Croak Croak Croak"

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.