Venkat wrote the book on Scala. This third installment of his series on Scala shows how Scala’s OO purity leads to simple, elegant code.
In this third part of the series we will explore the elegance and purity of the OO side of Scala.
Writing classes is one of the most common tasks that every Java programmer does. Let’s write a class Car in Java:
All we said in the above code was that we’d like to initialize a Car with a read-only property year and a read-write property miles. However, it took quite a few keystrokes to achieve that.
You may hear some Java programmers protest “But I never write those myself, I only declare the fields and with a right click, my IDE vomits the rest of the code.”
We don’t need that in Scala. Let’s write the above Car class in Scala. Go ahead, type this into a Car.scala file.
You can declare classes private if you like, but if you leave out the declaration, it defaults to the most common scenario—public class.
With that concise declaration, you made year a read-only field with a getter (and no setter). The miles has both a getter and a setter since you used var instead of val to define it.
Hey, but what about a constructor? In Scala, when you define a class you’re writing the primary constructor. Any code you directly place into the class becomes part of this constructor. Any fields and methods you define become part of the class.
You can write other constructors, called auxiliary constructors. Scala enforces that the auxiliary constructors call the primary constructor and only the primary constructor is allowed to pass data to the base (super). This helps reduce accidental code duplication among constructors and tightly manages the instantiation of the base and the derived parts of instances.
So the constructor is also done when you write the class. You may wonder what you will now do with all your free time. Go home, you don’t have to write redundant code in a smart language.
The getter/setters created by default do not follow the JavaBean convention. If you’d like, ask Scala to follow that convention using the @BeanProperty annotation:
When you mark a property with the @BeanProperty annotation, the Scala compiler will create the JavaBean-style getter for that property if it is declared val and, in addition, a setter if it’s declared a var.
You can get the year using either instance.year (or instance.year() if you like, but parentheses are optional) or using the JavaBean convention instance.getYear().
Now we can focus on adding some behavior to the class. Let’s add a drive method:
The drive method accepts a parameter, distance, and changes the miles accordingly. A good practice in Java is to declare the method parameters as final. Scala automatically treats all parameters as val (final). Any attempt to modify distance within the drive method will result in an error. Scala does not merely suggest good practices, it enforces them in quite a few places like this.
Let’s create an instance of the Car and take it for a drive.
We first create an immutable reference car that refers to a new instance of Car. We then get the value of year first using the Scala-style getter and then using the JavaBean style. Finally we print the value of miles before and after a call to the drive method. When you run the code, you will see the following output:
Since we made the miles a read-write property, we could change the miles using
If you’d rather make miles read-only from the outside, you can do that as well and provide a separate private field for access within the class. Let’s make that change and also reduce some clutter in the drive method definition.
Any attempt to set the values of miles, for example using car.miles = 20, will now result in a compilation error. Also, car.milesDriven = 20 is prohibited.
We saw how to create a class and instantiate an instance. In the first part of this series I mentioned that Scala is fully object-oriented. Since we’re on the topic of OO, now is a good time to revisit that.
Java has objects and primitives, and you work with them differently. In Scala you don’t have to treat them differently. For example, you can do the following on an int value:
You called the toString method on an int value 1, followed by the call to the abs method, and two calls to the to method. The output from this code is:
As an astute reader you may have a couple of concerns at this point. First, how does it do that? Second, is there a performance consequence?
Scala has wrapper classes like RichInt, RichDouble, etc. that represent the object form of their primitive counterparts. When you perform actions like 1.to(3), Scala quietly does an implicit conversion from the value (1) to a RichInt object. RichInt has quite a few convenient methods like to, abs, etc. specific to operations on integers.
Scala converts from primitive to the rich wrappers only based on the context. If you do not invoke any methods on a primitive, Scala will simply represent that as primitive at the bytecode level. So you don’t invoke any performance degradation in those cases, the conversion applies only when it’s called for.
Being a pure OO language, Scala does not directly allow static methods. In Java you generally write both instance methods (and fields) and static methods (and fields) within a class. This lacks separation of concern and clutters the class. Also, in your mind, you have to switch context between these two when you’re implementing or modifying a class. Scala eliminates these problems by providing a clear separation.
The Scala equivalent of a Java class with only static methods is a singleton. Recollect the Singleton pattern? A pattern that takes minutes to understand and months to get right, yep, that’s the one I’m talking about. It turns out that Singleton is mired with so many problems that an entire chapter and a slew of recommendations on how to handle it is written in Effective Java by Josh Bloch. Scala is effective Java so it handles it for you, and Singleton is a first-class citizen in Scala.
You create a singleton in Scala using the object keyword as shown here:
You don’t create an instance of Util, it’s the single instance. You can directly call methods on it, like so:
On the Java side, the methods of a singleton appear as static methods, so you can invoke them as such from outside Scala.
Scala also allows a class and a singleton to share the same name. In this case, the singleton is called a companion object. A companion object and the corresponding class are quite intimate and don’t have any encapsulation boundaries between them.
Let’s use this to create a class and a factory singleton (companion) to instantiate objects.
The Marker class prints a message in the constructor to indicate when an instance is created. We made the constructor of the Marker private by placing that keyword between the class name and the parameter list for the constructor. As a result we can’t create an instance of Marker directly in our code, however the companion object can.
In the companion object’s create method we check to see if a marker with the requested color already exists in a hashmap. We create an instance only if it does not exist, add it to the collection, and return an instance.
The code to use the Marker and its companion is shown next:
The first three requests are for three different colors and so three new instances will get created. The last request, however, is for a color that was already instantiated and so the cached instance from the hashmap will be returned as we can see from the output:
Scala greatly simplifies and streamlines the everyday task of OO programming by providing sensible conventions. It reduces the amount of code you have to write/maintain and brings clarity by separating instance-related members and class (static)-related members. We discussed the OO aspects of Scala in this part, and we’ll look at the functional style in the next.
Dr. Venkat Subramaniam is an award-winning author, founder of Agile Developer, Inc., and an adjunct faculty at the University of Houston.
He has trained and mentored thousands of software developers in the US, Canada, Europe, and Asia, and is a regularly-invited speaker at several international conferences. Venkat helps his clients effectively apply and succeed with agile practices on their software projects.
Venkat is the author of .NET Gotchas, the coauthor of 2007 Jolt Productivity Award winning Practices of an Agile Developer, the author of Programming Groovy: Dynamic Productivity for the Java Developer and Programming Scala: Tackle Multi-Core Complexity on the Java Virtual Machine. His latest book is Programming Concurrency on the JVM: Mastering synchronization, STM, and Actors.