Class

  • No need for public modifier, a class is not declared as public
  • Multiple classes in a source file with public visibility
  • Call a parameterless method with or without parentheses
    • Use () for a mutator method
    • Drop the () for an accessor method. To enforce this style, declare the method without ()
  • No need to define accessors for a var field
    • For a var field, a private field with its public accessors are generated in bytecode
    • In the case of private field, accessors become private too.
    • Note: Simple getters and setters are preferred over public fields. They can be evolved as needed. So custom getter/setter without changing the client of a class is possible. That is called the uniform access principle
    • Defining a field as val results in a read-only property
    • In the case of custom accessors, there should not be identical to var name
  • Parameters of the primary constructor turn into var fields, initialized by the passed arguments
  • Nest anything inside anything
    • functions inside other functions, and classes inside other classes

Define custom getter

def PROP: TYPE = …

Define custom setter

def PROP_=(VARIABLE: TYPE) …

Note: both _= are required without space between and before!

In the following class, important points of a class in Scala is illustrated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package org.devocative

object Education extends Enumeration {
  type Education = Value
  val Diploma, Bachelor, Master, PhD = Value
}

package employee {

  import java.util.{Calendar, Date}

  import org.devocative.Education.Education //----- also import Education.Education

  import scala.beans.BeanProperty //----------------try to import fully qualified (scala can be omitted)

  // although following class is not declared public, it is public
  class Person { outer => //-----------------------alias 'outer' = 'Person.this', useful for inner class

    val id: Int = Person.newUniqueNumber() //------need 'Person' qualifier

    var name: String = _ //------------------------field must be initialized, '_' means null

    var education: Education = _ //----------------Enumeration as a type need 'type' definition

    private[this] var birth_date: Date = _ //------object-private field

    @BeanProperty //-------------------------------generating getX & setX for Java interoperability,
    var weight: Int = 0 //-------------------------so it can't be private!

    var address: Person#Address = _ //-------------type projection, i.e. Address for any Person


    // --------------- A U X   C O N S T R U C T O R S

    def this(name: String, birthDate: Date) { //---use 'this' for constructor name
      this() //------------------------------------calling default primary constructor is mandatory

      this.name = name // -------------------------also outer.name = name
      this.birthDate = birthDate
    }

    def this(birthDate: Date) { // ----------------this must be declared after the above one!
      this(null, birthDate) // --------------------refer to the above one
    }

    def this(name: String) {
      this(name, null)
    }


    // --------------- A C C E S S O R S

    def age = { //----------------------------age is an accessor, preferred to declare without ()
      val bd: Calendar = Calendar.getInstance
      bd.setTime(birth_date)

      Calendar.getInstance.get(Calendar.YEAR) - bd.get(Calendar.YEAR)
    }

    def birthDate: Date = birth_date

    def birthDate_=(date: Date) {
      if (date == null || date.compareTo(new Date) <= 0)
        birth_date = date
      else
        throw new IllegalAccessException("Invalid ‌Birth Date")
    }


    // --------------- F U C T I O N S  &  P R O C E D U R E S

    // since 'birth_date' is an object-private field, can't access other.birth_date
    def >(other: Person): Boolean = birth_date != null && birth_date.compareTo(other.birthDate) < 0

    def +(other: Person): Set[Person] = Set[Person](this, other)

    def sendMsg() {
      println(s"Hi $name ($id), You are $age years old, and your weight is $getWeight")
    }

    override def toString: String = name


    // --------------- I N N E R   C L A S S

    class Address(var province: String, var city: String) { // class with primary constructor
      override def toString: String = s"${outer.name} lives at $city, $province"
    }
  }


  // --------------- C O M P A N I O N   O B J E C T

  object Person {
    private var lastNumber = 0

    def newUniqueNumber(): Int = {lastNumber += 1; lastNumber}

    def apply(name: String): Person = new Person(name)

    def apply(name: String, birthDate: Date): Person = new Person(name, birthDate)
  }
}
  • Both the class and its companion object
    • can access each other’s private features
    • must be defined in the same source file

Now, following code shows a sample code to use the above class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Calendar

import org.devocative.Education
import org.devocative.employee.Person

// ...

val cal: Calendar = Calendar.getInstance
cal.set(Calendar.YEAR, 1980)
cal.set(Calendar.MONTH, 1)
cal.set(Calendar.DATE, 1)

val jane = new Person
//jane.id = 1 ------------------------------ERROR: val variable
jane.birthDate = cal.getTime
jane.name = "Jane"
jane.setWeight(60) //-----------------------also jane.weight = 60
println(jane.age) //------------------------OUTPUT: 38

jane.sendMsg() //---------------------------OUTPUT: Hi Jane, You are 38 years old, and your weight is 60

val joe = new Person("Joe", {cal.add(Calendar.YEAR, -1); cal.getTime}) // calling aux constructor
joe.address = new joe.Address("P", "C") //--instantiate inner class by object reference
println(joe > jane) //----------------------also joe.>(jane), output: true
println(joe.address) //---------------------OUTPUT: Joe lives at P, C

jane.address = joe.address //---------------it is ok because of type projection
println(jane.address) //--------------------OUTPUT: Joe lives at P, C
jane.education = Education.Bachelor

var children = Person("Joe Junior") + Person("Sara") + Person("Janet")
  • Line 25 shows the same result as line 28! Although Address is type projected, the instance is created by joe!
  • Line 31, first + is the function defined in Person, while the second one is defined in scala.collection.Set

Object

  • Java’s static fields and methods replacement in Scala
    • A class can have a companion object with the same name in the same file
    • Define an apply method in the companion object for constructing new instances of the related class
  • It can extend other classes or traits
  • No constructor parameters
  • Suitable for
    • Utility functions or constants
    • Sharing a single immutable instance efficiently
    • Coordinate some service using single instance (the singleton design pattern)