This series is all about the taste of Scala.
It is best suitable for all the newbies in Scala.
You may also like: Scala Beginner Series (1) : Basics
In the previous part, we covered:
- Values, variables, expressions and types
- Functions and recursion
- The Unit type
This article will cover the object oriented nature of Scala language.
Scala has the same concept of a class as we have in other languages.
Classes in Scala are blueprints for creating objects. Members of classes are methods, values, variables, objects, traits and super-classes. We can access the members and fields of classes using dot operator.
Also, all fields and methods of classes are public by default, unless we restrict their visibility with the
protected modifiers. We don’t have to specify
public access modifier explicitly.
A minimal class definition is simply the keyword
class and an identifier. The keyword
new is used to create an instance of the class.
Classes in Scala can take arguments — those are the constructor arguments. When we define a class, we can also define its constructor signature.
Beware that constructor arguments are not class fields . Parameters without
var are private values, visible only within the class. Thus the following code will generate error:
Thus in order to promote constructor arguments to class fields, we can put a
var before the argument name.
When a class inherits from another, it means it extends another. This lets a class inherit members from the one it extends and provides the code re-usability.
To carry out Scala inheritance, we use the keyword
In the above example of two classes,
Person class is base class or super-class and
Employee class is derived class or sub-class.
extends clause has two effects:
- It makes
Employeeclass inherit all non-private members of
- It makes the type
Employeea subtype of the type
Scala also allows only single class inheritance just like Java.
Scala has the same subtype polymorphism that we have seen in other statically typed object oriented languages. In subtype polymorphism, instance of subclass can be passed to a base class.
At compile time, compiler only knows that we are calling
demo() method of
Element object. But at run time, the most derived method i.e.
demo() method of
ArrayElement is called. A derived class can, of course, override their super class methods.
Scala also has a concept of an abstract class that is similar to Java’s abstract class, which contains both abstract and non-abstract methods and cannot support multiple inheritances. They can’t be instantiated as well.
To implement Scala abstract class, we use the keyword
abstract against its declaration:
It is also mandatory for a child to implement all abstract methods of the parent class. Abstract methods of abstract class are those methods which do not contain any implementation.
Traits in Scala are similar to Java’s Interfaces. Classes and objects can extend traits, but traits cannot be instantiated and therefore have no parameters.
Traits are like partially implemented interfaces. It may contain abstract and non-abstract methods. It is possible that all methods are abstract, but it should have at least one abstract method.
Classes which extend the traits have to implement the abstract methods of the trait, but need not to implement the concrete methods of the trait.
To define trait, we use the
Scala has single-class inheritance and multi-trait mixing. It is possible to extend any number of Scala traits with a class or with an abstract class.
Scala also has the concept of anonymous classes much like Java.
We already saw how to implement parent class’s declared methods. A less formal way to provide the implementation for a parent class’s methods is with an anonymous class, a non-reusable and nameless class definition.
To define a one-time anonymous class, instantiate the parent (and potentially abstract) class and follow the class name and parameters with curly braces containing the implementation.
The result is an instance that does extend the given parent class with a one-time implementation, but can be used like an instance from a traditional class definition.
To the compiler, anonymous class looks like this:
With anonymous classes, the compiler does a lot of work behind the scenes. This allows us to abstract that complexity away from our code.
There is no idea of
static in Scala, instead we have singleton objects.
An object is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val. The methods and fields declared inside singleton object are globally accessible, we don’t need an object to access them. We can import them from anywhere in the program. Also, we can’t pass parameters to its primary constructor.
A singleton object is declared using the
A singleton object can extend classes and traits. It also provides an entry for program execution. Without such an object, the code compiles but produces no output.
When a singleton object is named the same as a class, it is called a companion object, and the class is called companion class. The companion class-object pair has to be in a single source file. Either member of the pair can access its companion’s private members. We use a companion object for methods and values which are not specific to instances of the companion class.
I have entered
:paste mode here because I am working in the
REPL. Since companions need to be in same source file, so
:paste mode in
REPL allows companions to be defined together.
static members in Java are modeled as ordinary members of a companion object in Scala.
The Apply Method
Scala classes as well as Scala objects, both offer a default method that we name as the
apply method. We sometimes call it an injector method. We can also invoke this method without a name. Let me show you what I mean:
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.
We can use apply in companion objects as factory methods. The idea of the factory method is to construct class instances without
Scala case classes are like regular classes with a few key differences which we will go over. When the compiler sees the
case keyword in front of a
class, it generates code for us, with the following benefits:
- Scala case classes are immutable by default and decomposable through pattern matching.
- It does not use
newkeyword to instantiate object. This is because case classes have an
applymethod by default which takes care of object construction.
unapplymethod is generated, which lets us to use case classes in more ways in
- A default
toStringmethod is generated, which is helpful for debugging.
hashCodemethods are generated, which let us compare objects and easily use them as keys in maps. It is used to check the value equality of all individual member fields instead of just checking the reference equality of the objects.
- We can create a shallow copy of an instance of a case class simply by using the
copymethod. We can also change the constructor arguments.
- All the parameters listed in the case class are public and
valby default. It is possible to use
varin case classes but this is discouraged.
case object is like an
object, but just like a case class has more features than a regular class, a case object has more features than a regular object. Its features include:
- It’s serializable
- It has a default
- It has an improved
The biggest advantage of case classes and case objects is that they support pattern matching. Pattern matching is a major feature of functional programming languages, and Scala’s case classes and objects provide a simple way to implement pattern matching in match expressions and other areas.
You may also like: Pattern Match Anything in Scala
Stay tuned for our next part of Scala Beginner Series where we will cover the functional nature of Scala. You don’t want to miss it!