4 Fun and Useful Things to Know about Scala's apply() functions
image by flughafen
Hire me to supercharge your Hadoop and Spark projects
I help businesses improve their return on investment from big data projects. I do everything from software architecture to staff training. Learn More
One confusing element of Scala for beginners is classes that can be constructed without the new
keyword, like this val p = Person()
. The reason for this is a special function called the apply
function. In fact it is not actually a constructor, take note: you’ll hear me say this quite a few times (it’s important!).
Apply Function Basics
A simple use of apply
is to define it on an Object
. This lets you call the Object
as if the object itself was a function. Here’s an example:
This is really done with some compile time sugar which translates function calls to Greet()
into calls to Greet.apply()
. So really the apply function is a simple short-hand that lets you save a few characters.
Scala however loves the apply function, and so it is used in several interesting ways. Here are 4 interesting ways you can use (or do use) the apply()
function in Scala.
1. Automatic Apply Functions for Case Class Companion Objects
This is the typical use case that makes many new Scala developers mistake apply()
for a constructor.
Scala provides a special kind of class called the case class
. I’ll not go into details here, but consider the case class a simple version of a regular Java class that is designed to provide a simple set of fields and not much more.
Here’s a really simple case class:
Case classes provide you with an automatically generated apply
function on their companion object that you can use like a constructor. This is very confusing as it looks just like a constructor and quacks like a constructor, but it is not a constructor.
In fact, a built-in Scala macro has automatically generated an apply method to match our case class fields. The constructor still exists, and can be used normally too.
2. Mistaking Apply() for a Constructor Can Cause Runtime Issues
Note that although case classes use apply
like a constructor it is not a constructor. This is a really important distinction. It will cause issues if you’re required to implement a specific constructor and will typically causes confusing stack traces at runtime if you’re using a library that makes use of reflection.
Some libraries use reflection to ‘sniff’ your classes and make sure they can be constructed as expected, and may dynamically construct them. Any apply
functions will not register as constructors, no matter how you use them in your code. Instead you’ll get a runtime error and will need to figure out why.
So in our earlier example, this doesn’t work: val greeting = new Greet("bob")
.
Remember, apply
is just a special function that lets us call the parent object directly, like a function. It has nothing to do with object orientation, classes, or constructors in the slightest.
3. Using Apply() as a clever class builder
Just because you commonly see apply functions alongside case classes in place of a constructor does not mean it has to return an instance of it’s companion class. In our original example Greet.apply()
returned a string, not an instance of a class called Greet
.
So for example, this is totally kosher, although admittedly confusing:
I like to use this behavior to provide the correct inherited class from the base class’s companion object.
Here’s a simple example:
So instead of having a builder or factory, we use the companion object to create a streamlined pseudo-constructor.
Also note that like all functions in Scala and Java, you can override apply
several times and have each function do something different. So in my example I could take a database connection string in a different apply function instead of a configuration object.
4. Apply Functions are used for Anonymous Functions
In scala you can create an anonymous function like so:
In reality you’re actually just creating an object with an apply function. Scala does this for you automagically. You can even call apply()
directly if you like:
In fact you’re actually just creating an instance of a built in Function
class, in this case Function1[String, String]
. You can create it yourself if you like:
5. Bonus - Put an apply function in a class (not an object!)
You may have noticed that Function1
is a class, not an object. You can add apply functions to your own classes too!
So there you have it, a quick tour of Scala’s apply function. We typically see it on objects, but you can put it on classes too! It is a very powerful concept, and along with the unapply
function it allows you to do even more interesting things with pattern matching, but that is a topic for another time.
How do you use apply()
in your code? Let me know in the comments below.