Module 03
Module 03
Module 03
1 / 17
Agenda
1. Class Definitions
2. Class Constructor
4. A Rational Class
5. Checking Preconditions
6. Referencing Self
8. Auxiliary Constructors
In Scala, this includes objects, traits, and package objects (more later)
class DemoWithFieldsAndMethods {
val x: Int = 10
val y: Int = x * 2
def timesY(a: Int): Int = a * y
}
3 / 17
Constructor
class DemoWithParams(name: String) {
println(s"Constructing for $name")
// rest of class...
}
Code in the class (not in defs) becomes the primary constructor code, runs
when a new instance is constructed
4 / 17
Parameters, Fields and Parametric Fields
Constructor parameters are private (actually private[this]), also vals
private and protected are keywords, there is no public keyword, that's the
default for vals and defs (but not for constructor parameters)
5 / 17
A Rational Class
As in, let's make something to represent a Rational number from what we
know so far:
6 / 17
Checking Preconditions in the Constructor
class Rational(val n: Int, val d: Int) {
require(d != 0, "Zero denominator!") // precondition
override def toString: String = s"R($n/$d)"
}
val half = new Rational(1, 2)
// half: Rational = R(1/2)
If you use require and the predicate fails, you will get an
IllegalArgumentException thrown
7 / 17
Referencing Self
Could also write require(this.d != 0, "Zero denominator!")
But it does have symbolic method names, (and operator precedence rules
for first character)
http://scala-lang.org/files/archive/spec/2.11/06-expressions.html#infix-
operations 9 / 17
In x and Symbolic Methods
class Rational(val n: Int, val d: Int) {
require(d != 0, "Zero denominator!")
override def toString: String = s"R($n/$d)"
// symbolic rational addition
def +(other: Rational): Rational =
new Rational(
this.n * other.d + this.d * other.n,
this.d * other.d
)
}
10 / 17
Adding an Int to a Rational
Strategy one: construct a Rational from an Int
Auxiliary constructors are quite limited, they can only call another
constructor
11 / 17
Introducing: Companion Objects
An object in the same source file with the same name as the class (or trait)
Shares private state and behavior with the class (and vice versa)
12 / 17
Because It's a Companion
It can access private behavior on the class
We can make the constructor private and use the factory methods only:
class Rational private (val n: Int, val d: Int) { // note position of private
...
}
object Rational {
def apply(n: Int, d: Int): Rational =
new Rational(n, d) // companion can still call new
def apply(i: Int): Rational =
new Rational(i, 1)
}
val fifth = Rational(1, 5) // R(1/5)
val five = Rational(5) // R(5/1)
val half = new Rational(1, 2) // not allowed!
13 / 17
Adding an Int to a Rational
Strategy two, overloading:
Implicit conversion has single "in" type and single "out" type, e.g.
Companion objects for types involved in type problem are one of the
places Scala looks for implicits
Implicits can also be used for val, object and class (more on implicits
later)
16 / 17
Exercises for Module 3
Find the Module03 class and run it with ScalaTest
This time it will be all green, but read the instructions and you will see
that you need to uncomment the tests, write code, and make it compile
and pass again
As you get each section working, there may be a section following that
also needs to be uncommented
17 / 17