How to Construct Objects in Scala
In order to get a better understanding of how Scala works, we will examine a few Scala examples and the corresponding byte code.
Join the DZone community and get the full member experience.Join For Free
Scala's object-oriented programming style comes with some syntactic sugar and a few tricks.
In order to get a better understanding of how Scala works, we will examine a few Scala examples and the corresponding byte code (in reality, the Java code resulting from decompiling the class files).
Simplest possible example, an empty class. No surprises here, just a class with a no-args constructor. By the way, as it is revealed by this example, the default visibility modifier in Scala is public
Class With Parameters but Not Fields
Class with a constructor that takes parameters: it results in a class with an all-args constructor.
The body of the class outside any method is considered part of the constructor definition.
Class With Parameters Promoted to Fields
When the parameters of the constructor are used by members of the class other than the constructor, those parameters are promoted to fields so they can be accessed.
It's worth noticing that the visibility of the fields generated is object-private ( private[this]), meaning that they cannot be accessed by other objects of the same class. For instance, this example won't compile:
Interestingly, the modifier private[this] has no meaning in the JVM and therefore it is compiled to private.
Class With Parametric Fields
The keyword val can be used as a shorthand to define at the same time a parameter and a field with the same name. The field is accompanied by a getter named as the field.
However, everything is designed to create the illusion that there are no methods. In this case, the getter is defined as a parameterless method and as a consequence the client is oblivious to whether they are calling a val or a def.
This is the way Scala supports the uniform access principle:
the client code should not be affected by a decision to implement an attribute as a field or method.
Unfortunately, this cannot be appreciated in the Java version as it shows the method defined with parentheses, an empty-paren method, given that parameterless methods don't exist in the realm of the JVM.
However, the following example illustrate how this works in Scala:
Now it would be possible to implement the method equals as the fields can be accessed through the corresponding public getters.
Parametric Fields With var
It's also possible to define parametric fields with the keyword var. In this case, a setter named field_= is also generated.
Similarly to the previous example, the setter field_= is interpreted as the assignment operator to create the illusion that there is no method. Although in this case, it is also possible to invoke the setter directly:
The visibility of the generated getter/setter is the same as the visibility of the corresponding val/var
Class With Custom Getter/Setter
In the Java tradition, we can also create our own getter/setter methods. However, the access to the variables is still done through the synthetic getter/setter.
Here's an example of how to use the new methods. Notice how getN can be invoked with or without parentheses
Objects are compiled to two classes:
- a singleton with the suffix $ added to its name and containing the actual functionality
- a class with static forwarders to access the singleton functionality.
A companion object in Scala is an object that's declared in the same file as a class, and has the same name as the class. A singleton and a forwarder are created like in the previous case.
The only difference is that now the singleton implements AbstractFunction (with the corresponding -arity) and Serializable.
Interestingly, none of the functionality implemented by the singleton is available in Scala as FractionVal does not have any reference to FractionVal$.
For instance, the method apply is not available whereas toString uses the implementation given by Object.toString instead of FractionVal$.toString
Case classes are regular classes with tons of features.
- by default, the constructor parameters are val
- for each case class, the compiler creates a companion object
- the resulting classes implement Serializable
- methods equal and hashCode are overridden based on the attributes of the class fields
Here's the structure of the resulting classes:
It is also possible to define case objects. Again, a singleton and a forwarder are created. The most remarkable thing is that the singleton implements Serializable and Product.
Traits are compiled as interfaces
The rules for concrete classes also apply to abstract classes
Package objects are treated as Objects. Two classes are created: a singleton called package$ and the forwarder class called package
Published at DZone with permission of Francisco Alvarez, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.