Copyright © Cay S. Horstmann 2015
This work is licensed under a Creative Commons Attribution 4.0 International License
class Point(val x: Double, val y: Double) { def this() { this(0, 0) } def move(dx: Double, dy: Double) = new Point(x + dx, y + dy) def distanceFromOrigin = math.sqrt(x * x + y * y) override def toString = s"(${x}, ${y})" }
move
, distanceFromOrigin
, toString
override
when overriding methods (here Object.toString
)()
for parameterless accessor methodsx
, y
with getters, call p.x
, p.y
val
fields
move
yields a new object!s"Hello, ${x}!"fills the value of the expression
{x}
into the string
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(s"Welcome to (${x}, ${y})") // Printed whenever a new point is constructed ... }
var
for mutable instance variables
class Point(var x: Double, var y: Double)
p.x
, you don't know whether x
is a field or a methodvar
or val
, without getter/setters
class BankAccount { private val balance = 0.0 ... }
val
/var
:
class Point(x: Double, y: Double) // No accessors for x, yIf
x
, y
are used beyond the constructor, they become private instance variables.
class Mystery(val x: Double, y: Double) { val z = x + y def foo = x + z }
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 in infix p * 2
:
, it is right-associative. E.g. if we want to define power operator ab
, use a **: b
a **: b **: cis
a **: (b **: c)See Unit 8 for a more natural example.
apply
method can be invoked with (...)
notation. We've seen this with maps. 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(s"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)
trait Logger { def log(msg: String) { println(msg) } } class Point extends Logger { def move(dx: Double, dy: Double) = { log(s"Moving (${x} ${y}) by ${dx} ${dy}") new Point(x + dy, y + dy) } ... } trait TimestampLogger extends Logger { override def log(msg: String) { super.log(s"${new java.util.Date()} ${msg}") } } trait ShortLogger extends Logger { val maxLength = 15 override def log(msg: String) { super.log( if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...") } } val pt = new Point(3, 4) with TimestampLogger with ShortLogger pt.move(5, 6) // Prints Wed Mar 11 20:17:01 CET 2015 Moving (3.0 ...
val pt2 = new Point(3, 4) with ShortLogger with TimestampLogger { override val maxLength = 20 } pt2.move(5, 6)
What does it log?
Wed Mar 11 20:17:01 CET 2015 Moving (3.0 ...
Wed Mar 11 20:17:...
Wed Mar 11 20:17:01 CET 2015 Moving (3.0, 4.0 ...
Wed Mar 11 2...
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(hours: Int, minutes: Int) { private val minutesSinceMidNight = ... ... }
Supply parameterless methods hours
, minutes
hours
and minutes
into mutable fields, so that the following is ok:
val start = new Time(13, 0) start.minutes = 15What did you have to do?
_=
:
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
.