Inheritance Details in Scala

Raffi Khatchadourian (based on “Scala for the Impatient” 2nd Edition by Cay Horstmann)

September 22, 2020

Inheritance Details

Highlights

Extending a Class

class Employee extends Person {
    var salary = 0.0
    // ...
}

Overriding a Method

class Person(val name: String) {
    override def toString = s"${getClass.getName}[name=$name]"
}

val p = new Person("Juan")
println(p)

Outputs:

Person[name=Juan]

Protected Fields and Methods

Overriding Fields

Recall that a field in Scala consists of:

  1. A private instance field.
  2. Accessors/mutators for the field.

You can override a val (or parameterless def) with another val field of the same name.

Then, the subclass will have a private field and a public accessor, which overrides the one from the super class.

class Person(val name: String) {
    override def toString = s"${getClass.getName}[name=$name]"
}

class SecretAgent(codename: String) extends Person(codename) {
    override val name = "secret"
    override def toString = "shh"
}

val s = new SecretAgent("1234")
println(s)

Outputs:

shh

Abstract Classes & Methods

Can also override an abstract def with a val:

abstract class Person {
    def id: Int // No implementataion, specific to each kind of person.
    // ...
}

/**
 * A concrete implementation of Person.
 */
class Student(override val id: Int) extends Person {
    // The student ID is supplied by the ctor.
    // ...
    override def toString = s"Student with ID: ${id}"
}

val s = new Student("54321")
println(s)

Outputs:

Student with ID: 54321

override keyword not required when providing an implementation for an abstract method.

Abstract Fields

Can also have abstract fields, i.e., fields without an initial value:

abstract class Person {
    val id: Int // No initializer---abstract field and accessor.
    var name: String // No initializer---abstract field, accessor, and mutator.
    // ...
}
class Employee(val id: Int) extends Person { // Has a concrete id property.
    var name = "John Doe"  // and a concrete name property that can be concretized.
    override def toString = s"Employee ${name} with ID: ${id}"
}

val e = new Employee(5)
println(e)

Outputs:

Employee John Doe with ID: 5

What if we did not override name? What would happen?

Scala Inheritance Hierarchy

  • Primitive type classes and Unit, i.e., (), extend AnyVal.
    • These are value classes.
    • AnyVal has no methods; it’s just “marker.”
  • Reference types extend AnyRef.
    • In the bytecode, this is java.lang.Object.
    • Defines methods useful for concurrency (e.g., locking).
  • Any is the hierarchy root. It defines:
    • isInstanceOf[]
    • asInstanceOf[]
    • Equality and hash code methods.
  • All Scala classes implement the marker interface ScalaObject.
  • Null is a singleton type with the value null.
    • You can assign null to any reference.
  • Nothing has no instances.

Scala inheritance hierarchy diagram

Nothing

Object Equality

Value Classes

A value class:

  1. Extends AnyVal.
  2. Has a primary ctor with only one (val) parameter, i.e., the “wrapped” value, and no body.
  3. Has no other fields or ctors.
  4. Automatically provides equals and hashCode methods that simply delegate to the wrapped value.

Example

class MilTime(val time: Int) extends AnyVal {
    def minutes = time % 100
    def hours = time / 100
    override def toString = f"$time%04d"
}