Scala Advanced Features

by Stephane Micheloud, November 2010

[Home]
[Back]

In this article we present several examples of advanced features in the Scala programming language.

Overriding class members

Scala behaves differently to Java when overriding class members (see [1], 5.1.3). For instance a Scala programmer may override not only the value but also the type of a class member. We illustrate those differences with three small code examples.

  1. Given the two Java class declarations

    class Foo {}
    class Bar extends Foo {}

    we consider the following class hierarchy

    abstract class A {
        Foo foo; // [1]
    }
    class A1 extends A {
        int foo; // [2]
        A1() { foo = 1; }
    }
    class A2 extends A {
        A2() { foo = new Bar(); }
    }
    abstract class B extends A {
        int foo; // [3]
    }
    class B1 extends B {
        A21() { foo = 2; }
    }

    where the abstract field foo of type Foo in base class A (marked as [1]) is hidden by a member with the same name declared in A1 and B (marke as [2] resp. as [3]), two subclasses of A.

    That's legal Java code! We give here a few test cases together with their output:

    class Main {
        public static void main(String[] args) {
            new Main();
        }
        Main() {
            A a = new A1();
            System.out.println("a.foo="+a.foo); // null
            a = new A2();
            System.out.println("a.foo="+a.foo); // Bar@..
            B b = new B1();
            System.out.println("b.foo="+b.foo); // 2
            a = b;
            System.out.println("a.foo="+a.foo); // null
        }
    }

    The Scala compiler rejects the code below which implements the same class hierarchy

    class Foo
    class Bar extends Foo
    
    abstract class A {
      val foo: Foo // [1]
    }
    class A1 extends A {
      val foo: Int = 1 // [2]
    }
    class A2 extends A {
      val foo: Bar = new Bar()
    }
    abstract class B extends A {
      val foo: Int // [3]
    }
    class B1 extends B {
      val foo = 2
    }

    and prints out the following error messages:

    Main.scala:9: error: overriding value foo in class A of type Foo;
     value foo has incompatible type
      val foo: Int = 1
          ^
    Main.scala:15: error: overriding value foo in class A of type Foo;
     value foo has incompatible type
      val foo: Int
          ^
    Main.scala:18: error: overriding value foo in class A of type Foo;
     value foo has incompatible type
      val foo = 2
          ^
    three errors found
  2. Let's now revisit a slightly modified version of our class hierarchy:

    abstract class A {
        Foo foo; // [1]
    }
    class A1 extends A {
        A1() { foo = new Foo(); }
    }
    class A2 extends A {
        A2() { foo = new Bar(); }
    }
    abstract class B extends A {
    }
    class B1 extends B {
        B1() { foo = new Foo(); }
    }

    Again we interest us in the abstract field foo of type Foo declared in base class A (marked as [1]). When declaring a subclass of A a Java programmer has to initialize foo with some value whose type is a subtype of Foo but he can't restrict that choice by specifying a more specific type for foo, for instance in A2.

    class Main {
        public static void main(String[] args) {
            new Main();
        }
        Main() {
            A a = new A1();
            System.out.println("a.foo="+a.foo); // Foo@..
            a = new A2();
            System.out.println("a.foo="+a.foo); // Bar@..
            B b = new B1();
            System.out.println("b.foo="+b.foo); // Foo@..
            a = b;
            System.out.println("a.foo="+a.foo); // Foo@..
        }
    }

    In contrary a Scala programmer may override the type of foo in a subclass of A (marked as [1] and [2]):

    abstract class A {
      val foo: Foo
    }
    class A1 extends A {
      val foo: Foo = new Foo()
    }
    class A2 extends A {
      val foo: Bar = new Bar() // [1]
    }
    abstract class B extends A {
      val foo: Bar // [2]
    }
    class B1 extends B {
      //val foo = new Foo() // error: value foo has incompatible type
      val foo = new Bar()
    }

    foo now must be initialized with a value whose type is a subtype of Bar in every subclass of B.

    The result for test case 4 differs from the output for the Java example.

    object Main extends App {
      var a: A = new A1()
      println("a.foo="+a.foo) // Foo@..
      a = new A2()
      println("a.foo="+a.foo) // Bar@..
      var b: B = new B1()
      println("b.foo="+b.foo) // Bar@..
      a = b
      println("a.foo="+a.foo) // Bar@..
    }
  3. And in Scala we can go one step further: we may define an abstract type in base clase A (marked as [1]) and then specify the concrete type of foo (and typically of more class members) in a subclass of A:

    abstract class A {
      type T <: Foo // [1]
      val foo: T
    }
    class A1 extends A {
      type T = Foo
      val foo: T = new Foo()
    }
    class A2 extends A {
      type T = Bar
      val foo: T = new Bar()
    }
    abstract class B extends A {
      type T = Bar
    }
    class B1 extends B {
      //val foo = new Foo() // error: value foo has incompatible type
      val foo = new Bar()
    }

    We get the same results for the test cases from example 2.

Partial functions



  

  

References

  1. The Scala Language Specification (SLS), Version 2.8
    Martin Odersky, November 2010

About the Author

Stephane's Picture
Stéphane Micheloud is a senior software engineer. He holds a Ph.D in computer science from EPFL and a M.Sc in computer science from ETHZ. At EPFL he worked on distributed programming and advanced compiler techniques and participated for over six years to the Scala project. Previously he was professor in computer science at HES-SO // Valais in Sierre, Switzerland.
[Top]

Other Articles