left-icon

Scala Succinctly®
by Chris Rose

Previous
Chapter

of
A
A
A

CHAPTER 7

Classes and Objects

Classes and Objects


Scala is a language that combines functional and object-oriented paradigms. The object-oriented mechanisms are designed to allow us to create modules of reusable code called classes. A class is a collection of data and methods that operate on that data. We will see that classes are very similar to the objects we have been using all along—for example, the MainObject. The main difference between a class and an object is that a class is designed to have multiple instances of objects created from it, whereas an object is the only instance of its class.

If you are not familiar with Java’s object-oriented programming mechanisms, I strongly suggest you read up on them. Scala is a language designed to address many of the shortcomings of the Java language. Object-oriented programming is all about defining our own data structures to reduce the overall amount of code in our projects and to allow our projects to be maintainable and scalable.

A class is a blueprint for creating objects. Objects are called instances of the class. All of the data types in Scala are objects, including Int and Double. When we specify a new var or val, we are using objects. The fields of our objects must be initialized, and, unlike with Java, in Scala we cannot create an object with uninitialized fields.

Object-oriented programming allows us a mechanism to combine data and functions that operate on this data. In Java-speak, these are called member variables and member methods. Member variables are variables that belong to the objects, and the member methods are the functions that the objects are able to perform. These can be accessed using the “.” operator, such as someString.length. Or, if you have an object with a member variable called height, you can use someClass.height to access this variable.

Variable names are used to point to objects. They are references to objects.So an object, such as the number 100, can potentially be referred to by many variables.

Classes

We can add classes to our existing files, but if the classes are complicated and contain a lot of code, it is sometimes better to add a separate code file to our project. We will look at two methods for adding new classes to our projects—in the first, we add a new file for the class. This keeps the code for our class separate from the other classes, but it means that our project has multiple files. Using the second method, we add new classes to existing files. In Scala, we can define multiple classes per file. This has the advantage of minimizing the number of files in our project, but the classes are all mixed together and this can sometimes become difficult to maintain. As a general rule of thumb, if a class is required by other classes, or if a class is complex and requires many methods, the class should be defined in a separate file. Otherwise, if the class is very simple and only used by one other class in our project, we might define the new class inside the same file as the existing class.

Adding a new class

Method 1: Adding a new class file

To add a new class file to your project, click File > New > Scala Class, as in Figure 17. You can also add a new class by right-clicking the project in the Package Explorer and selecting New > Scala Class.

Adding a New Class File

Figure 17: Adding a New Class File

You will be presented with the New File box, as in Figure 18. In this box, you can name your class in the box provided and click Finish. It is common to name classes with a leading uppercase letter because this makes it easy to differentiate identifiers that are class names from identifiers that are functions or variables.

Adding a New Class Step 2

Figure 18: Adding a New Class Step 2

Eclipse will create a new file in your project and write the basic skeleton of a new class with the name provided, as in Code Listing 65. 

Code Listing 65: A Blank Class

class MyNewClass {

}

In the Package Explorer, you will note that we now have a new file added to the src folder (as in Figure 19). We can edit the code for our new class by doubling-clicking its name to open the code in the code view.

Class File in Package Explorer

Figure 19: Class File in Package Explorer

Method 2: Adding class to an existing file

We can also code a new class directly into any existing object or class file. Code Listing 66 assumes we did not add the class called MyNewClass in a separate file and shows us a basic code file for the MainObject of a new program with the code for the MyNewClass class defined above the code for the MainObject.

Code Listing 66: Defining a Class in an Existing File

// Definition of a new class

class MyNewClass {

}

// Definition of the MainObject

object MainObject {

 

  def main(args: Array[String]): Unit = {

    println("All good?")

  }

}

Scala is fairly flexible with regards to where we can define a new class. Code Listing 67 shows three examples of new classes defined at different points in our MainObject file.

Code Listing 67: Adding Classes to an Existing File

// Define a new class outside:

class Class1 {

}

// Definition of the MainObject

object MainObject {

  // Define a new class local to MainObject

  class Class2 {

  }

 

  def main(args: Array[String]): Unit = {

    // Define a new class local to MainObject.main

    class Class3 {

    }

  }

}

Code Listing 67 shows the declaration of three classes, each having a different scope. Class1 is defined outside the body of the MainObject object, and it has program-wide scope (exactly the same as adding the class to a new file). Class2 is defined inside the body of the MainObject object. This class is not accessible to outside classes, but it can be used in any methods within the MainObject. Class3 is defined inside the body of the main method. This means that the class does not exist outside the main method.

Class syntax

The syntax for a class begins with the keyword class and is followed by the name of the class and a code block surrounded by { and }. Code Listing 68 shows the basic skeleton of a do-nothing class. This is the basic class that Eclipse will write for us when we add a new class to our project, or the smallest amount of code we are required to write in order to define a new class.

Note: In Java, class files and the classes in them should share the same name. However, this restriction is not part of Scala, and we are free to name our classes anything we like (within reason) and to define multiple classes and objects per file.

Code Listing 68: Skeleton of a Class

class ClassName {

  // Body of the class

}

A class is simply a blueprint. It defines what types of variables and functions the objects built from it will have. When we create an object from our class blueprint (instantiate the class), the object is called an instance of the class. In order to instantiate a class, we use the new keyword in a similar way as with Java. Code Listing 69 shows two examples of creating an instance from a class called ClassName (this Code Listing is not complete and will not compile and run).

Code Listing 69: Creating an Instance from a Class

// Two ways to create an instance from a class

// called ClassName:

   

// Shorthand method:

var classInstance = new ClassName

   

// Verbose method:

var classInstance: ClassName = new ClassName

In Code Listing 69, the first method for creating an instance is to specify either var or val (depending on whether you want to change the variable or create a constant object). Next, we use an identifier for the new object, in this case classInstance, and we set the identifier equal to new ClassName. This is a shorthand method for creating an instance, and it should look very familiar. This is exactly the same as when we define other basic objects such as Int and Boolean.

The second method is slightly more verbose than the first. We can optionally specify the data type for our new object. In the previous example, this is not particularly useful, but Code Listing 70 shows another example of this verbose method, this time using inheritance. Code Listing 70 defines an instance of SomeChild called myInstance, but the data type is SomeParent. We will soon look at inheritance in more detail.

Code Listing 70: Example of Verbose Method with Inheritance

// Define a parent class:

class SomeParent {

 

}

// Define a child class:

class SomeChild extends SomeParent {

 

}

// Definition of the MainObject

object MainObject {

  def main(args: Array[String]): Unit = {

   

    // Define a SomeParent object, which is

    // presently an instance of SomeChild

    var myInstance: SomeParent = new SomeChild

  }

}

Code Listing 71 shows a basic example of a class complete with a few fields. The listing also shows that we access the fields using the dot syntax.

Code Listing 71: Basic Class with Some Fields

// Definition of the Atom class

class Atom {

  // Three fields, or member variables:

  var electronCount: Int = 0

  var name: String = "Unknown"

  var symbol: String = "NoSymbol"

}

object MainObject {

  def main(args: Array[String]): Unit = {

    // Instantiate a new member of Atom class:

    val hydrogen = new Atom

 

    // Set the fields/member variables:

    hydrogen.electronCount = 1

    hydrogen.name = "Hydrogen"

    hydrogen.symbol = "H"

 

    // Access the fields/member variables:

    println("Name: " + hydrogen.name + " (" + hydrogen.symbol + ")")

    println("Electrons: " + hydrogen.electronCount)

  }

}

Note that in Code Listing 71, the “.” means “the field belonging to,” so that hydrogen.symbol means the symbol field belonging to the hydrogen object. Each instance of a class has its own fields; if we created a second object from the Atom class, iron, for example, the fields hydrogen.symbol and iron.symbol would be two distinct fields that would not necessarily have the same values.

In Scala, we have abstract classes, just like in Java. Unlike Java, Scala has abstract variables. If a class variable is not assigned a value in the class definition, the class must be marked as abstract. This is also true of methods. Methods can be abstract (or have no definition) in Scala, and any class with one or more abstract methods is itself abstract. In Code Listing 71, all variables have been given a default value in the Atom class. Also note that the variables are public by default, which means they can be accessed inside the main method without marking them as public (whereas in Java, all members of a class are private by default). We will look at abstract class in more detail later, but this is the reason that I have set each of the members of the Atom class to default values 0, Unknown, and NoSymbol.

As a second example, Code Listing 72 shows a basic Box class. The class consists of two fields, sideLen1 and sideLen2, that we will use to define a box of size sideLen1*sideLen2. We will expand this class by adding some member methods.

Code Listing 72: Basic Box Class

class Box {

  var sideLen1: Int = 0

  var sideLen2: Int = 0

}

Val vs. var in object-oriented programming

It is worth pointing out a particularly detailed nuance of the val vs. var mechanism. If we have a val that refers to some object, and the class has fields marked as var, we can change the object’s fields even though the object itself is immutable. The val means the assignment of the object itself is immutable—it does not refer to the member fields of the object (which may or may not be val themselves). Code Listing 73 shows an example of this behavior.

Code Listing 73: Val vs. Var and Objects

class Box {

  var sideLen1: Int = 0

  var sideLen2: Int = 0

}

object MainObject {

  def main(args: Array[String]): Unit = {

    // Define a new val:

    val immutableBox = new Box

   

    // Define a new var:

    var mutableBox = new Box

   

   

    // Set the fields of mutableBox

    mutableBox.sideLen1 = 12

    mutableBox.sideLen2 = 13

   

    // Set the fields of the immutableBox

    immutableBox.sideLen1 = 23

    immutableBox.sideLen2 = 14

    // Set the mutableBox to point to another Box.

    // This is fine because mutableBox is var:

    mutableBox = immutableBox

   

   

    // But the following illegal, we cannot reassign the

    // immutableBox, because it is val!

    immutableBox = mutableBox

   }

}

Notice that in Code Listing 73 we can change the fields of the object called immutableBox even though the object is val. But we cannot reassign the object to another Box (this reassignment is illustrated by the final line, which I highlighted in red because it is illegal). It is very important to understand what the val and var refer to when we use the terms in our projects.

Private modifier

When we declare a field in a class, we can mark it as private. This prevents any outside objects from interacting with the field. In Scala, members that have no modifier are assumed to be public, so that external objects can interact with the fields. In object-oriented programming, it is recommended that we hide details of the way our classes work because that gives us the flexibility of changing the way the class works without having to worry about other objects accessing the fields directly.

Code Listing 74 shows the code for our Box class, but now the sideLen1 and sideLen2 fields have been marked as private (highlighted in Yellow). Notice that we are not able to set the sideLen1 field from the main method because the MainObject object is not part of the Box class, and the fields are private. Therefore, the final line of the main method is illegal, and I have highlighted it in Red.

Code Listing 74: Private Fields

class Box {

  private var sideLen1: Int = 0

  private var sideLen2: Int = 0

}

object MainObject {

  def main(args: Array[String]): Unit = {

   

    // Create an instance from the box class:

    var myBox = new Box

   

    // It is no longer legal to access the sideLen1

    // or sideLen2 fields outside the Box class.

    // The following line is illegal!

    myBox.sideLen1 = 100

   }

}

Member methods

A member method is a function that instances of a class are able to perform. When we define a member method for a class, we access all of the class’s private fields. In order to add a member method to a class, we use def to define a method inside the body of the class. We have seen this many times already, particularly when the main method is a method that we have defined for our MainObject objects.

Code Listing 75: Basic Member Methods

class Box {

  private var sideLen1: Int = 0

  private var sideLen2: Int = 0

 

  // Member method called area:

  def area(): Int = {

    return sideLen1 * sideLen2

  }

 

  // Member method called perimeter:

  def perimeter: Int = { // No params, brackets are optional

    2*(sideLen1 + sideLen2) // Implicit return

  }

}

Note: When we use parameters in methods, they are val from the point of view of the method, so they cannot be changed. This means that even when a var is passed to a method from within the body of the method, the value is immutable. Put another way, Scala does not support C# style out or ref parameters.

Note: The return statement in functions is not needed. Functions return or evaluate to the last value computed. In Scala, it is typical that we try to write functions so that only a single line returns the result. This means that we tend to ensure a Scala function evaluates to a single return statement, and the return keyword is often not used.

Code Listing 75 shows two example methods for our Box class. The method area returns Int and takes no parameters. Likewise, the method perimeter takes no parameters and returns Int. When a function takes no parameters, we can leave out the parameter parentheses, as in the perimeter method.

We can also leave out the code block if a function is only a single statement. This means the area function of the Box class could have been written as the following single line of code (the perimeter method could also be a single line): def area(): Int = sideLen1 * sideLen2.

If a method returns Unit, i.e. no return value, we can use the brackets in a similar way as with Java by leaving out the return type of Unit all together. Code Listing 76 shows a new method we can add to our Box class. This method prints out the sideLen1 and sideLen2 fields, but it does not return anything, and I have left out the return type of Unit.

Code Listing 76: Unit Is Optional

def printMe() {

  println("Box Sides: " + sideLen1 + " " +

      sideLen2)

}

Constructors

A constructor is a special member method that we call when we use the new operator. In Scala, the constructor for a class is defined by specifying a parameter list in the class’s declaration. Code Listing 77 shows an example of our Box class, complete with a constructor that takes two integers, side1 and side2. We set the member fields sideLen1 and sideLen2 to the parameters passed. Then, in the main method, when we create an instance of our class using the new operator, we can pass the lengths as parameters.

Code Listing 77: Constructors

// Box class with constructor:

class Box(side1: Int, side2: Int) {

 

  // Set the member fields to the values

  // passed as paramaters:

  private var sideLen1: Int = side1

  private var sideLen2: Int = side2

 

}

object MainObject {

   def main(args: Array[String]): Unit = {

  

     // Call the Box constructor and pass parameters:

     var myBox: Box = new Box(10, 12)    

    

   }

}

In order to define multiple constructors, we use the this keyword to overload the constructor. This is useful for defining several constructors that take different parameter lists. Code Listing 78 shows an example of our Box class with three different constructors.

Code Listing 78: Defining Multiple Constructors

// Class with 3 constructors:

class Box(side1: Int, side2: Int) {

 

  private var sideLen1: Int = 0

  private var sideLen2: Int = 0

  def this() {

    this(-1, -1// Call main constructor with -1

  }

 

  // This constructor takes one parameter,

  // it sets both sideLen fields to the

  // same value:

  def this(side: Int) {

    this    // Call the constructor which takes no arguments

 

    // After we have called any fully defined constructor

    // inside the body of a new constructor, we are free to

    // reassign the values of the fields:

    sideLen1 = side

    sideLen2 = side

  }

}

object MainObject {

   def main(args: Array[String]): Unit = {

    

     // Create a box by calling the main constructor:

     var box1 = new Box(10, 10)

     // Create some boxes by calling the parameter-less constructor

     var box2 = new Box

     var box3 = new Box()

    

     // Create a box by calling the constructor which takes one

     // parameter:

     var box4 = new Box(100)

   }

}

Notice that in Code Listing 78 the first thing inside the additional constructors is a call to some other, fully defined constructor. The main constructor for our class is defined with the class declaration as requiring two Int parameters. This means that when we define new constructors, they must provide a call to this main constructor in some way. We can either call the main constructor directly, e.g., this(-1, -1), or we can call some other constructor that in turn calls the main constructor, e.g., this in the third constructor. Note that the third constructor calls the parameterless constructor, which in turn calls the main constructor.

Note: Function overloading is a technique in which we create multiple functions with the same name. We can have as many functions with the same name as we need, but the functions must have unique parameter lists.

Note: We can have two or more fields with the same name in different scopes. This is the same as in Java, but in Scala we can also define two or more variables with the same name in nested scopes. The inner variable is said to shadow the outer one because the variable defined in the outer scope is not available until the inner one goes out of scope.

Note: Scala does not have static member variables. We can, however, create singleton objects—these are objects built from classes of which there is only instance. Singletons are simply Scala objects. We can add as many as we like in exactly the same way that we have been adding our MainObject object to our programs. Singletons are similar to classes in every way—except that we do not instantiate them because they already represent the only instance of the singleton.

Note: In addition to allowing singleton objects, in Scala we can also create companion objects. A companion object is an object that has the same name as a class and that is defined in the same file as that class. Companion objects can be used in a similar way to static member methods and fields in Java.

Inheritance

In terms of inheritance, Scala offers mechanisms similar to Java’s. We can create a parent class with functions and fields, then inherit from this parent to a more specific child class. Code Listing 79 shows an example of inheritance. In order to inherit from a parent class, we use the extends keyword.

Code Listing 79: Inheritance

// Main parent class:

class GameObject(objName: String, xPos: Int, yPos: Int) {

  val name = objName

  var x = xPos

  var y = yPos

 

  def print {

     println("Name: " + name + " Pos: " + x + "x" + y)

  }

}

// PointObject class is a child class inheriting from

// GameObject, but it adds a score, which is the amount

// of points the player receives for collecting the object.

class PointsObject(objName: String, xPos: Int, yPos: Int, scoreValue: Int)

  extends GameObject(objName, xPos, yPos) {

 

  // Define an extra field to record the score

  // this object is worth:

  var score: Int = scoreValue

}

// Another example class, the MoveableObject also inherits from the

// GameObject parent, but it defines several methods for moving

// around.

class MoveableObject(objName: String, xPos: Int, yPos: Int)

  extends GameObject(objName, xPos, yPos) {

 

  def moveUp = y = y - 1

  def moveDown = y = y + 1

  def moveLeft = x = x - 1

  def moveRight = x = x + 1

}

// We can also inherit from other child classes:

class Player extends MoveableObject("Player", 100, 100) {

 

}

object MainObject {

 

  def main(args: Array[String]): Unit = {

    // Create a GameObject:

    val gameObject = new GameObject("Some generic object", 54, 123)

   

    // Create a Points object:

    val pointsObject = new PointsObject("Coin", 65, 18, 500)

   

    // Create a Player object:

    val player: Player = new Player

  

 

    // All objects inherit from the GameObject class, so

    // we can call any methods from that class or access any

    // public member fields:

    player.print

    pointsObject.x = 90

 

    // In addition, the pointsObject has a score field, and

    // the player has several extra methods defined for moving

    // which it inherited from the MoveableObject parent:

    pointsObject.score = 1000

   

    player.moveUp

    player.moveLeft

    player.moveLeft

    player.print

  }

}

Inheritance Hierarchy from Code Listing 79

Figure 20: Inheritance Hierarchy from Code Listing 79

Figure 20 is an illustration of the hierarchy defined by Code Listing 79. The main parent is the GameObject class. Both the PointsObject and the MoveableObject classes inherit from this parent. This means they have access to the x and y integers from the parent and also to the print method. The Player class inherits from the MoveableObject class, therefore it inherits the x and y from the MoveableObject’s parent along with the additional methods defined for the MoveableObject class. In this example, the Player class does not specify any additional fields or methods, but it could.

Notice that in Code Listing 79, when we specify that our new classes extend an existing class, we must call the parent class’s constructor class Player extends MoveableObject("Player", 100, 100). This means that the Player class has access to all public members from the parent class and that we should call the parent’s constructor with the values "Player", 100, 100 for the parameters.

We can access the parent’s methods and fields with the super keyword in the same way that we do in Java. So, from the Player class’s body, we can access the moveUp method by calling super.moveUp.

Abstract classes

I will briefly explain what an abstract class is and how they are defined in Scala, but if you are not already familiar with other object-oriented languages (C++, Java, C#, etc.), I strongly recommend that you become familiar with at least one of them. A lot of technique is involved with object-oriented programming, and this e-book must necessarily concentrate on only Scala and how it differs from some of the other languages.

An abstract class is a class that cannot be instantiated. It can be used as a parent class, and child classes can define meanings for the abstract parts of the parent class. For example, we can create a generic Shape class with computePerimiter and computeArea methods, but the generic parent class itself does not define these methods. We can then inherit from the parent class in a child class such as Circle and Square, in which we define the body of the parent class’s functions.

Code Listing 80: Abstract Class

abstract class Shape {

  // Define an abstract field

  type id 

 

  // Define some abstract methods

  def computeArea: Float

  def computePerimeter: Float

}

// Define a Child Class

class Circle(radius: Float) extends Shape {

  var id: Int = 0

  def computeArea: Float = {

    return 3.14159265359f * radius * radius

  }

  def computePerimeter: Float = {

    return 2 * 3.14159265359f * radius

  }

}

object MainObject {

 

  def main(args: Array[String]): Unit = {

 

    var circle: Circle = new Circle(6)

 

    println("Area of Circle: " + circle.computeArea)

  }

}

Code Listing 80 shows an abstract parent class called Shape. The class contains an undefined field called id using the type keyword and two abstract methods—computeArea and computePerimiter. Notice that the class is marked as abstract. When we extend from this parent, we must define all of these abstract elements in the child class or else the child class must itself be marked abstract. The Circle class inherits from the Shape class and provides a definition for each of the parent’s abstract elements. This means the Circle is not abstract, and we can create an instance from it as illustrated in the main method of Code Listing 80.

Note: Scala also offers a similar mechanism to Java’s interfaces called traits. The interested reader should look up traits in the Scala documentation. Find more information at http://docs.scala-lang.org/tutorials/tour/traits.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.