06. Objects (enums) - RobertMakyla/scalaWiki GitHub Wiki
Singleton
Scala has no static methods, but it has Singletons:
object Account {
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1; lastNumber }
}
Note: newUniqueNumber is a function with () because it changes the state.
Object's constructor is called when object is used for the first time :
Account.newUniqueNumber()
Object may contain everything that a class contains.
Object may extend class or trait. But it can never have constructor parameters !!!
Account.newUniqueNumber() // 1
Account.newUniqueNumber // 2 - ok without () cause it's parameterless
Account.newUniqueNumber() // 3
Class may have Companion Object (equivalent of java's instance methods and static methods)
{
class Account {
val id = Account.newId()
}
object Account { // companion object
private var lastId = 0
private def newId() = { lastId += 1; lastId }
}
}
Note: class and companion object: (interview)
- have the same names
- see each other private properties / private methods - but not object-private things
- companion object see private constructor of the class
- are not in scope of each other (class cannot call directly companion's methods)
- must be located in the same source file
Objects extending classes or Traits
Extending object gets all the features of class/trait, e.g. :
abstract class UndoableAction(val description: String) {
def undo(): Unit // abstract method in abstract class. There is no '=' or '{}'
def redo(): Unit
}
object DoNothingAction extends UndoableAction("Do nothing") {
def undo() {} // 'override' not needed cause super method is abstract
def redo() {}
}
The apply method (interview)
It is common for objects to have apply methods. Typically it returns instance of companion class.
Thanks to apply() method, while instantiating a class we don't need to use new
Array(10)
// this calls apply(10) yielding Array[Int] with a single integer element : 10
new Array(10)
// this invokes this(10) constructor. Result is Array[Nothing] with 10 null elements
e.g. :
{
class Account(val id: Int, var balance: Int) {
println("Account created: Id="+id+", balance="+balance)
def deposit(amount:Int) {balance += amount}
}
object Account {
private var lastId = 0;
private def newId() = {lastId += 1 ; lastId}
def apply(initBalance: Int) = new Account(newId(), initBalance)
}
val a = Account(100) // id = 1
val b = Account(50) // id = 2
val c = Account(130) // id = 3
}
Application objects
Instead of starting application with main(args:Array[String]) : Unit
object Hello {
def main(args: Array[String]) {
println("Hello, World!")
}
}
I can extend App (which even has access to args)
object Hello extends App {
if (args.length > 0) println("Hello, " + args(0))
else println("Hello, World!")
}
Enumerations
object TrafficLightColor extends Enumeration {
val Red, Yellow, Green = Value // val Red = Value ; val Yellow = Value; etc..
}
It's the same as:
object TrafficLightColor extends Enumeration {
val Red = Value(0, "Red")
val Yellow = Value(1, "Yellow")
val Green = Value(2, "Green")
}
class Value can be provided with custom id/name, or both:
object TrafficLightColor extends Enumeration {
val Red = Value("Stop") // same as Value(0, "Stop") cause first ID is always 0
val Yellow = Value(10) // name "Yellow", ID 10
val Green = Value("Go") // name "Go", ID 11, cause it's 10 + 1
}
Now, I can use it :
import TrafficLightColor._
def doWhat(color: TrafficLightColor) = {
if (color == Red) "stop"
else if (color == Yellow) "hurry up"
else "go"
}
Iterating through enums:
for (c <- TrafficLightColor.values) println(c.id + ": " + c)
Getting ID for enum:
TrafficLightColor.Red.id
TrafficLightColor.withName("Red").id
TrafficLightColor withName("Red") id
TrafficLightColor withName {"Red"} id
Exercises
Define a Point class with a companion object so that you can construct Point instances as Point(3, 4), without using new.
{
class Point private (val x: Int, val y: Int) {
print(s"Creating point ($x, $y)")
}
object Point {
def apply(x: Int, y: Int) = new Point(x, y)
}
val p = Point (1, 2)
}