
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, toStringoverride when overriding methods (here Object.toString)() for parameterless accessor methodsx, y with getters, call p.x, p.yval 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 IllegalArgumentExceptionTime 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.