- Hands-On Data Analysis with Scala
- Rajesh Gupta
- 956字
- 2021-06-24 14:51:05
Object-oriented programming using Scala
In the object-oriented paradigm, you think in terms of objects and classes. A class can be thought of as a template that acts as a basis for creating objects of that type. For example, a Vehicle class can represent real-world automobiles with the following attributes:
- vin (a unique vehicle identification number)
- manufacturer
- model
- modelYear
- finalAssemblyCountry
A concrete instance of Vehicle, representing a real-world vehicle, could be:
- vin: WAUZZZ8K6AA123456
- manufacturer: Audi
- model: A4
- modelYear: 2009
- finalAssemblyCountry: Germany
Let's put these attributes in action in Scala.
Go to the Scala/SBT console and write the following lines of code:
- Define Vehicle Scala class as per the preceding specifications:
scala> class Vehicle(vin: String, manufacturer: String, model:
String,
modelYear: Int, finalAssemblyCountry: String)
defined class Vehicle
- Create an instance of Vehicle class:
scala> val theAuto = new Vehicle("WAUZZZ8K6AA123456", "Audi", "A4",
2009, "Germany")
theAuto: Vehicle = Vehicle@7c6c2822
Following is the IntelliJ Scala worksheet:

The object-oriented approach puts data and behavior together. The following are core tenets of object-oriented programming:
- Encapsulation: It provides a mechanism to shield implementation details and internal properties
- Abstraction: It provides constructs such as classes and objects to model real-world problems
- Inheritance: It provides constructs to reuse implementation and behavior using subclassing
- Polymorphism: It facilitates mechanisms for an object to react to a message based on its actual type
Let's look at encapsulation and abstraction in Scala Read-Evaluate-Print-Loop (REPL). We'll use Scala's construct class to define a template for a real-world Vehicle, as shown in the following code:
- Let us define Vehicle class, this is an example of abstraction because we are taking real-world complex entities and defining a simple model to represent them. When internals of implementations is hidden then it is an example of encapsulation. Publicly visible methods define behavior:
scala> class Vehicle(vin: String, manufacturer: String, model: String, modelYear: Int, finalAssemblyCountry: String) { // class is an example of abstraction
| private val createTs = System.currentTimeMillis() // example of encapsulation (hiding internals)
| def start(): Unit = { println("Starting...") } // behavior
| def stop(): Unit = { println("Stopping...") } // behavior
| }
defined class Vehicle
- Now let create an instance of Vehicle. This is also an abstraction because Vehicle class is a template representing a simplified model of real-world vehicles. An instance of Vehicle represents a very specific vehicle but it is still a model:
scala> val theAuto = new Vehicle("WAUZZZ8K6AA123456", "Audi", "A4",
2009, "Germany") // object creation is an example of abstraction
theAuto: Vehicle = Vehicle@2688b2be
- Perform start behavior on the object:
scala> theAuto.start()
Starting...
- Perform stop behavior on the object:
scala> theAuto.stop()
Stopping...
To reiterate the main points aforementioned, the ability to define a class is an example of abstraction. Inside the class, we have an attribute called createTs (creation timestamp). The scope of this attribute is private and this attribute cannot be accessed from outside the class. The ability to hide internal details is an example of encapsulation.
Now let's look at inheritance and polymorphism in Scala REPL. We'll define a new class called SportsUtilityVehicle by extending the Vehicle class, as shown in the following code:
- Define SportsUtilityVehicle class that provides an extension to Vehicle class:
scala> class SportsUtilityVehicle(vin: String, manufacturer: String, model: String, modelYear: Int, finalAssemblyCountry: String, fourWheelDrive: Boolean) extends Vehicle(vin, manufacturer, model, modelYear, finalAssemblyCountry) { // inheritance example
| def enableFourWheelDrive(): Unit = { if (fourWheelDrive) println("Enabling 4 wheel drive") }
| override def start(): Unit = {
| enableFourWheelDrive()
| println("Starting SUV...")
| }
| }
defined class SportsUtilityVehicle
- Create an instance of SUV object but assign to Vehicle type object, this is permissible because every SUV object is also a Vehicle:
scala> val anotherAuto: Vehicle = new SportsUtilityVehicle("WAUZZZ8K6A654321", "Audi", "Q7", 2019,
"Germany", true)
anotherAuto: Vehicle = SportsUtilityVehicle@3c2406dd
- Perform start behavior on the object, on doing so the object exhibits the behavior of an SUV class. This is the polymorphism property facilitated by the object-oriented paradigm:
scala> anotherAuto.start() // polymorphism example
Enabling 4 wheel drive
Starting SUV...
Inheritance is a powerful construct that allows us to reuse code. We created an instance of SportsUtilityVehicle and assigned it to a type of vehicle. When we invoke the start method on this object, the runtime system automatically determines the actual type of object and calls the start method defined in SportsUtilityVehicle. This is an example of polymorphism, where we can treat objects as base types; however, at runtime, the appropriate behavior is applied depending upon the true type of the object.
The following is a UML diagram with a more formal representation of the inheritance relationship:

It captures the following important properties:
- The Vehicle is a super-class or base-class
- SportsUtilityVehicle is a sub-class that extends the Vehicle base-class
- This relationship can be envisioned as a parent-child relationship
This diagram shows that Vehicle is a base class and the SportsUtilityVehicle subclass extends this base class. The subclass adds its own additional attributes and behavior. One way to look at inheritance is as a generalization-specialization construct. The base class represents a generalized set of attributes and behavior. The derived class or subclass adds its specialization by either altering some of the base class behaviors or adding its own.
The following is a screenshot of the same example in IDE:

IDEs such IntelliJ help us to visualize many of the properties of classes and objects in a user-friendly way. A good IDE certainly acts as a great productivity tool. In the preceding example, the IDE screen is divided into the following three parts:
- Structure: Structural properties of classes and objects, such as methods and attributes
- Source code: Source code in the context
- Runtime: Output from the execution of the program