Objects: apply Methods in Companion Objects (Scala 3 Video)
In Scala, you can also use companion objects to create constructors for a class. Technically they’re not constructors, but in the end they work just like constructors. If you’ve heard of the Factory Method in OOP, putting this type of code in an object
is similar to that. Other languages and tools refer to this as creating “builders.”
The process
The way you do this is you create one or more methods named apply
in the companion object. Each apply
method creates a different way that instances of your class can be created.
For example, assuming that you want to create constructors for a Person
class:
- Define a
Person
class andPerson
object in the same file - Make the
Person
class constructor private - Define one or more
apply
methods in theobject
to serve as factory methods (which work just like constructors)
Example 1
For Steps 1 and 2, create the class
and object
in the same file, and make the Person
class constructor private:
// note the 'private' keyword here
class Person private(val name: String):
// define any instance members you need here
object Person:
// define any static members you need here
Then for Step 3, create one or more apply
methods in the companion object:
class Person private(val name: String):
override def toString = name
object Person:
// the “constructor”
def apply(theName: String): Person = new Person(theName)
Given this code, you can now create new Person
instances as shown in these examples:
val bert = Person("Bert")
val a = List(Person("Bert"), Person("Ernie"))
In Scala 2 a benefit of this approach was that it eliminated the need for the new
keyword when creating new instances of a class. But because new
isn’t needed in most situations in Scala 3, you may want to use this technique because you prefer this factory approach. In fact, I’ve seen that several experienced developers like to create their class “constructors” this way. Also note that the new
keyword is required in the apply
method to create a new Person
instance.
How this works
The way this works is that an apply
method defined in a companion object is treated specially by the Scala compiler. Essentially, there’s a little syntactic sugar baked into the compiler so that when it sees this code:
val p = Person("Fred Flintstone")
one of the first things it does is to look around and see if it can find an apply
method in a companion object. If it does find a matching apply
method in a companion object — i.e., an apply
method that takes one String
parameter — the compiler turns that code into this code:
val p = Person.apply("Fred Flintstone")
You can demonstrate this for yourself by putting this code into the Scala REPL.
Because of this, apply
is often referred to as a factory method or perhaps a “builder.” Technically it’s not a constructor, but it works just like one.
Example 2: More factory methods!
When you want to use this technique to provide multiple ways to build a class, define multiple apply
methods with the desired signatures, as shown in this OOP example:
class Person private(var name: String, var age: Int):
override def toString = s"$name is $age years old"
object Person:
// three ways to build a Person
def apply(): Person = new Person("", 0)
def apply(name: String): Person = new Person(name, 0)
def apply(name: String, age: Int): Person = new Person(name, age)
That code creates three ways to build new Person
instances:
println(Person()) // is 0 years old
println(Person("Bert")) // Bert is 0 years old
println(Person("Ernie", 22)) // Ernie is 22 years old
This text comes from my book, Learn Scala 3 The Fast Way!.
Update: All of my new videos are now on
LearnScala.dev