Reader small image

You're reading from  Learning Scala Programming

Product typeBook
Published inJan 2018
Reading LevelBeginner
PublisherPackt
ISBN-139781788392822
Edition1st Edition
Languages
Tools
Right arrow
Author (1)
Vikash Sharma
Vikash Sharma
author image
Vikash Sharma

Vikash Sharma is a software developer and open source technology evangelist. He tries to keep things simple, which helps him write clean and manageable code. He has invested a large amount of time learning and implementing Scala code, and he has authored video courses for Scala. He works as a developer at SAP Labs.
Read more about Vikash Sharma

Right arrow

Chapter 7. Next Steps in Object-Oriented Scala

"I was born not knowing and have only had a little time to change that here and there."

– Richard Feynman

The idea of companion objects gave us the feeling that it's important to know how your programming language treats the constructs you write. Suppose you were given a task to generate a case class with some sensitive parameters (by sensitive, we mean when trying to print that class, those sensitive fields should print some dummy values). What you are going to do in order to achieve that entirely depends on your knowledge of how Scala treats the case classes, and we learned that in the previous chapter. So, what now? Now it's time to do some composition as well as use inheritance. Remember, we talked about how we should think of a class as a type that we can define? It's a really useful and fun task to mix these types all together and try to make sense out of them and at the same time, add functionalities. That's why we have static typing, isn...

Composition and inheritance


In programming terms, to inherit or extend our classes we use the extends or with keywords. These are essential for the relationship between two or more classes or similar constructs. This association or relation between two classes or similar constructs can be in the form of inheritance (Is-A) or composition (Has-A). They are two different notions but they converge to some extent. In simple words, inheritance is a superclass-subclass relationship where the subclass inherits the implementation of the superclass, whereas composition is when a class depends on another object to provide some or all functionality. With an inheritance relationship, you can use the subclass object wherever superclass is expected. Think of it as this relationship between a Dictionary and a Book class:

class Book(val title: String) 
class Dictionary(name: String) extends Book(name) { 
  // data and behavior 
} 

We can picture the Book and Dictionary relationship as shown in the following...

Class inheritance


You already know that inheritance plays an important role in good object-oriented design. We are lucky enough to have constructs such as classes with names, and we can increase the possibility of relating those with other classes by using inheritance. Inheritance is about forming a meaningful hierarchy of classes to solve the purpose of code reuse. And mark my words, I mentioned meaningful hierarchies. I'll justify my words later. Let's take a look at how we can extend classes to make a hierarchy.

Extending classes

We use the extend keyword to inherit a class. Let's see our Book example to understand this:

class Book(val title: String){ 
  // data and behaviour for Book 
} 
 
class Dictionary(name: String) extends Book(name) { 
  // data and behaviour for dictionary 
} 
 
object BookApp extends App { 
  val dictionary = new Dictionary("Collins") 
  println(dictionary.title) 
} 

The result is as follows:

Collins 

We can see that Dictionary inherits from Book or Dictionary and...

Default and parameterized constructors


The primary constructor for any class defined in Scala is the body itself. It means that whatever you declare and define inside a class body gets instantiated when you make an instance of it. There are other ways to define secondary/auxiliary constructors as well. Take a look at the following case classes:

import java.time.LocalDate

case class Employee(name: String, id: String, contact: String, email: String) 

case class StartUp(name: String, founder: Employee, coFounders: Option[Set[Employee]], members: Option[List[Employee]], foundingDate: Option[LocalDate]) 

We can see two case classes named Employee and StartUp. You may wonder why Employee is specific to our StartUp class. The StartUp case class takes a few attributes such as founder, coFounder, members, and foundingDate. So, for creating instances of these case classes, we have to provide values for each member. In this case, if someone on the client side wants to use this case class and does not...

Traits


What are traits? For those coming from a Java background, it's tempting to see them as interfaces, but in reality they are something different. Trait constructs may look similar but are of a different nature to interfaces in Java. The meaning of the word trait is: a distinguishing quality or characteristic, typically one belonging to a person. One of the purposes of traits is the same. What if you want to add a particular characteristic to our hierarchy of classes or a single class? You can do this by extending or mixing in a trait. It's easier to say that we mix-in traits rather than extend from them. How are these two different? We'll talk about this as we go along but for now, let's take a look at how we define a trait in Scala:

trait Socialize { 
  
  //people who socialise, greets. 
  def greet(name: String) = "Hello " + name
}

Look at it this way. One of the qualities of people who tend to socialize well is that they greet you wholeheartedly when they meet you. In programming...

Traits as mix-ins


The way we do trait mix-ins is no different than inheriting any class in Scala; the only difference is that you can mix-in more than one trait and for that we have this nice keyword called with. Why do we call it mix-in? We could have called it something else. Well, yes but this explains almost everything you can do with traits. It's easy to modify or add up behaviors to an already existing functionality or construct without affecting already existing behavior. We'll see that in a bit. Traits can be used in a variety of use cases such as:

  • Composable mix-ins; to make already existing interfaces richer
  • Stackable modifications

Traits as composable mix-ins

By composable mix-ins we mean that we can create an instance of a particular type, with mix-ins of a trait, that can have certain additive functionalities. If you're thinking why would we want to do that, then the answer is maybe you want to add some particular behavior that makes sense to your functionality and you want it to...

Linearization


The reason why multiple inheritances become a burden when we try to implement them is due to the diamond problem. Take a look at the following image:

Diamond problem

Here, suppose we have an abstract class named Language, that has a method named sayHello. Two traits, named British and Spanish, extend the abstract Language class and define their own implementation of the sayHello method. Then we create a trait, named Socializer, that mixes in the other two traits with a super call to the sayHello method implementation. Now, confusion occurs as to which implementation of sayHello is getting called when we invoke this method. The primary reason for this problem is there's no multiple inheritance in Java, but Scala supports a form of multiple inheritance through trait mix-in. The concept Scala uses to resolve the problem of super calls is linearization. Let's first code for the problem and see it's behavior, then we'll understand linearization and the rules that justify the behavior...

Packaging and importing


One of the important aspects of object-oriented programs is how we define modular, reusable, and hierarchical structures. We're allowed to put all the code that we write, constructs like classes, traits, and objects, in some particular package. By using packaging and visibility rules, we can make our code more to reason about, means to expose some method to other classes or  and we get structured and modular code as an added advantage. There are a couple of ways you're allowed to write package statements in Scala; we'll take a look at those.

Package statements

We can write package statements at the beginning of the file. One of the simplest examples is as follows:

package country 
 
class Country(val name: String) { 
  import Country._ 
 
  val populationsMap  = scala.collection.mutable.Map[Int, Double]() 
 
  def showAveragePopulation() = println(averagePopulation(this.populationsMap.values))
 } 
 
object Country {
   def averagePopulation(populations: Iterable[Double...

Visibility rules


There are times when we don't want to let another class or similar construct use a few members. Here, we can use Scala's provided access modifiers. How we achieve control over the accessibility of members of our classes/traits/objects is through private, public, or protected access modifiers. Take a look at the following example:

package restaurant 
 
package privaterestaurant { 
 
  case class Dish(name: String) 
 
  trait Kitchen { 
    self: PrivateRestaurant => 
 
    private val secret = "Secret to tasty dish" //Think of a secret logical evaluation resulting in value, we don't want to expose. 
 
    def cookMyDish: Option[Dish] = Some(Dish(secret)) 
 
  } 
 
  class PrivateRestaurant extends Kitchen { 
 
    def serveDishWithSecret = Dish(secret) // Symbol secret is inaccessible from this place. 
 
    def serveDish = cookMyDish // Works fine 
  } 
 
} 

Here, we have some trait named Kitchen. It has a secret way of cooking a really tasty dish, but only for PrivateRestaurant...

Sealed traits


One good thing about sealed traits is that standard Scala library uses these constructs a lot, and you've also seen them many times so far. It's time to learn about them. We'll start with an example:

sealed trait Season 
 
case object Autumn extends Season 
case object Winter extends Season 
case object Spring extends Season 
case object Summer extends Season 
case object Monsoon extends Season 
 
object SealedApp extends App { 
  def season(season: Season) = season match { 
    case Autumn => println(s"It's Autumn :)") 
    case Winter => println(s"It's Winter, Xmas time!") 
    case Spring => println(s"It's Spring!!") 
    case Summer => println(s"It's summer, who likes summer anyway!") 
    case Monsoon => println(s"It's Monsoon!!") 
  } 
  season(Spring) 
} 

The result is as follows:

It's Spring!!" 

Here, we defined a sealed trait named Season. Then there're a few child season case objects extending from the sealed trait Season. By the way, case objects are like...

Summary


This chapter was more exciting. We learnt about inheritance in Scala and discussed composition and inheritance. It's really going to help us when we take design decisions about our implementation. Then we learnt about this amazing construct named traits and we tried various ways of using traits. Then we learnt about the concept of linearization which it helped us understand how super calls are resolved. Then we talked about packaging and importing which was exciting as Scala provides different ways to use them. Finally, we learnt about visibility rules and sealed traits. And after going through all these concepts, we can say with confidence that we now understand the object-oriented concepts in Scala. So, it's time for us to do some real functional programming. In the next few chapters our focus will be on using functions in Scala.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Learning Scala Programming
Published in: Jan 2018Publisher: PacktISBN-13: 9781788392822
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Vikash Sharma

Vikash Sharma is a software developer and open source technology evangelist. He tries to keep things simple, which helps him write clean and manageable code. He has invested a large amount of time learning and implementing Scala code, and he has authored video courses for Scala. He works as a developer at SAP Labs.
Read more about Vikash Sharma