Copyright © Cay S. Horstmann 2015
class Point(val x: Double, val y: Double) { def move(dx: Double, dy: Double) = new Point(x + dx, y + dy) def distanceFromOrigin = math.sqrt(x * x + y * y) override def toString = f"(${x}, ${y})" }
move
, distanceFromOrigin
, toString
override
when overriding methods (here Object.toString
)()
for parameterless accessor methodsx
, y
with getters, call p.x
, p.y
f"Hello, ${name}!"fills the value of the expression
name
into the string.
printf
-style formatters: f"${x}%8.2f"
Point
is an immutable class—can't change the val
fields:
p.x
, p.y
can only be used to read, not to mutate, the instance fields move
yields a new object!var
for mutable instance variables:
class Point(var x: Double, var y: Double)
p.x
, p.y
.var
or val
:
class BankAccount { private var balance = 0.0 ... }
class Point(val x: Double, val y: Double) { def this() { this(0, 0) } ... }
new Point(3, 4)
new Point()
class Point(val x: Double = 0, val y: Double = 0)
class Point(val x: Double, val y: Double) { println(f"Welcome to (${x}, ${y})") // Printed whenever a new point is constructed ... }
p.x
, you don't know whether x
is a field or a method.x
could have been defined as a field:
class Point(val x: Double, ...)
x
could have been a method:
class Point(...) { private val r = ... private val theta = ... def x = r * cos(theta) ... }
x op y
is the same as x.op(y)
.1 to 10 map (3 * _) filter (_ % 5 == 2)
class Point(...) { ... def *(factor: Double) = new Point(x * factor, y * factor) }
p.*(2)
or p * 2
, whichever you prefer.:
, it is right-associative. 1 :: 2 :: 3 :: NilThe parentheses group to the right:
1 :: (2 :: (3 :: Nil))
apply
method: map(key)
is map.apply(key)
.object
for singletons, static methods:
object Accounts {
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1; lastNumber }
// Aside: Use ()
since it mutates state
}
App
is like main
:
object MyApp extends App { println(f"Hello, ${args(0)}!") }
class Point { ... } object Point { ... }
apply
in companion object for factory methods:
object Point { def apply(x: Double, y: Double) = new Point(x, y) }
new
:
val p = Point(3, 4) * factor // prettier than new Point(3, 4)
Time
with read-only fields hours
and minutes
, a method toString
, and a method before(other: Time): Boolean
that checks whether this time comes before the other. A Time
object should be constructed as new Time(h, m)
, where h
is between 0 and 23 and m
between 0 and 59. If they aren't, call throw new IllegalArgumentException
.Time
objects and test your before
method.new Time(hrs)
. There are two different ways. What are they?In a new worksheet, reimplement the Time
class from the preceding exercise so that the internal representation is the number of minutes since midnight (between 0 and 24 × 60 – 1). Do not change the public interface.
Do not use var
or val
in the primary constructor!
class Time(h: Int, m: Int) { private val minutesSinceMidnight = ... ... }
Supply parameterless methods hours
, minutes
.
minutes
into a mutable field, so that the following is okay:
val start = new Time(13, 0) start.minutes = 15What did you have to do?
_=
, like this:
def minutes_=(newValue: Int) { ... }
start.minutes = -100How can you avoid that in the modified implementation?
Time
class have to make when switching from the original to the reimplemented class?Do the following with either the original or the reimplemented Time
class (your choice):
before
method of part 1 so that one can call:
if (t1 < t2) ...
-
that, given a Time
object, yields the number of minutes between them (between -1439 and 1439).Time
object can be constructed without calling new
.