Scala Tutorials Part #15 - The apply method
Originally Posted On : 29 Apr 2017
The Apply Method
This is part 15 of the scala tutorial series. Check here for the full series.
Index
The apply function
Apply
is just a mathematical name for applying a function to a value/set of values.
Let’s consider that f(x)
is a function with the following definition.
f(x) = x+1
In a programming language speak calling this function can be defined as
Call function/method f(x) with value x. Whereas in a mathematical notation,
this is usually referred as applying f(x)
to value x .
The wiki page explains this concept in detail.
Using apply in scala
This concept of apply is present in scala to create instances of classes in a unique way. Let’s take the below container class which represents a bunch of Strings in an array.
class Container {
//Hidden elements variable
private val elements = Array("Books","Pens","Laptops")
def apply(index:Int) = if(index<elements.length) elements(index) else "No element found"
}
We have a method called apply in the class which looks like an ordinary method, but it is not. We can now consume the class and call array index of elements as below.
val container = new Container
println(container(2))
The apply method is a special one which is called by default i.e calling container(2)
is the same as container.apply(2)
which would
yield the same result.
If we put the apply
method inside of a companion object then we do not need to instantiate since it would become a singleton, which then
simplifies our code to a great extent. We can use println(Container(2))
and then it would print the same result as above.
The compiler takes of translating the calls to the apply method.
Apply in case classes
From the infix notation chapter we take the complex number example again.
case class ComplexNumber(real: Double, imaginary: Double) {
def +(number: ComplexNumber): ComplexNumber =
ComplexNumber(real + number.real, imaginary + number.imaginary)
}
We can create instances simply as,
val a = ComplexNumber(2, 5)
val b = ComplexNumber(1, -3)
val c = a.+(b)
val d = a + b
If we look at the decompiled code behind this case class, there is an apply method which was auto generated and goes as follows.
public static ComplexNumber apply(double, double);
Code:
0: getstatic #20 // Field ComplexNumber$.MODULE$:LComplexNumber$;
3: dload_0
4: dload_2
5: invokevirtual #26 // Method ComplexNumber$.apply:(DD)LComplexNumber;
8: areturn
The apply
method is static because for case classes there is an automatic companion object that is generated with a lot of boilerplate methods which does not make sense as instance and hence they are created in the companion object.
Creating objects without new keyword
To recollect what we have learnt till now, we can create instances without the new
keyword in two ways.
- Case classes
- Object with apply method
Case classes are a simple way to create them. You have a companion object auto generated which creates a static apply method.
On compilation, all the calls such as ComplexNumber(2,1)
gets compiled to ComplexNumber.apply(2,1)
. We can create custom classes that
emulate only the apply behaviour of case classes by creating an apply method in the companion object .In the end it is just syntactic sugar,
but this is all done behind the scenes by the compiler without actually resorting to a constructor.
Apply is heavily used as part of the language library such as in the BigInt
class and other places. This method is very important
because it enables an object whether it is singleton or not to behave like a mathematical function.