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
- For a
- 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 byjoe
! - Line 31, first
+
is the function defined inPerson
, while the second one is defined inscala.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)