scala notes (2) - Class, Object, Package & Import and Inheritance
- Class
class Counter {
private var value = 0 // You must initialize the field, otherwise it's abstract class.
def increment() { value += 1 } // Methods are public by default
def current() = value
}
class Person {
var age = 0 // private age field, public set and get methods. if its val, private final field and only get method generated
}
- object-private field -> private[this] var value = 0
- Bean Property
class Person {
@BeanProperty var name: String = _ //generate two more methods, getName and setName
}
class Person(@BeanProperty var name: String)
- Primary Constructor & Auxiliary Constructor
class Person(val name: String = "", val age: Int = 0)
class Person private(val name: String = "", val age: Int = 0) //private primary constructor
primary constructor executes all statements in class definition
class Person {
private var name = ""
private var age = 0
def this(name: String) { // An auxiliary constructor
this() // Calls primary constructor
this.name = name
}
}
auxiliary constructor needs to call previously defined constructor before its own construction.
class Person(name: String, age: Int) { //
def description = s"$name is $age years old" //generate private[this] fields of name and age
}
class Person(name: String, age: Int){
println(s"$name is $age years old") //no fields generated
}
- nested classes
import scala.collection.mutable.ArrayBuffer
class Network {
class Member(val name: String) {
val contacts = new ArrayBuffer[Member]
}
}
val n1 = new Network; val n2 = new Network; n1.Member is different from n2.Member
solution is,
put class Member definition to Network companion object
or
use type projection, Network#Member
- Objects
- no parameter constructor of object
- good for utility functions or constants
- class and its companion object must be in the same source file. They can access each other's private features.
class Account {
val id = Account.newUniqueNumber()
private var balance = 0.0
def deposit(amount: Double) { balance += amount }
...
}
object Account { // The companion object
private var lastNumber = 0
private def newUniqueNumber() = { lastNumber += 1; lastNumber }
}
object extends class or trait
abstract class UndoableAction(val description: String) {
def undo(): Unit
def redo(): Unit
}
object DoNothingAction extends UndoableAction("Do nothing") {
override def undo() {}
override def redo() {}
}
- Enum (Enumeration)
object TrafficLightColor extends Enumeration {
val Red, Yellow, Green = Value
}
for (c <- TrafficLightColor.values) println(s"${c.id}: $c")TrafficLightColor(0) // Calls Enumeration.apply
TrafficLightColor.withName("Red")
- Packages & Imports
- same package can be in different source files.
- one source file can have multiple packages
- scope rules
package com {
package horstmann {
object Utils {
def percentOf(value: Double, rate: Double) = value * rate / 100
...
}
package impatient {
class Employee {
...
def giveRaise(rate: scala.Double) {
salary += Utils.percentOf(salary, rate) //parent package horstmann and ancestor package com are in scope
}
}
}
}
}
package path is not absolute in scala, use _root_.yourpackagepathchained package is not visible,
package com.horstmann.impatient {
// Members of com and com.horstmann are not visible here
package people {
class Person
...
}
}
- package object (functions can be put inside too)
package com.horstmann.impatient
package object people {
val defaultName = "John Q. Public"
}
package people {
class Person {
var name = defaultName // A constant from the package
}
...
}
- package visibility
package com.horstmann.impatient.people
class Person {
private[people] def description = s"A person with name $name"
or
private[impatient] def description = s"A person with name $name"
...
}
==========imports===========
- import java.awt._
- import java.awt.Color._
- import statement can be anywhere
class Manager {
import scala.collection.mutable._
val subordinates = new ArrayBuffer[Employee]
...
}
- import java.awt.{Color, Font} // selection
- import java.util.{HashMap => JavaHashMap} // rename
- import java.util.{HashMap => _, _} // import everything except HashMap
- Implicit Imports
import java.lang._
import scala._
import Predef._
StringBuilder is overridden by scala- Inheritance
class Employee extends Person {
var salary = 0.0
...
}
- override keyword to override non-abstract method
class Person {
...
override def toString = s"${getClass.getName}[name=$name]"
}
- call superclass method, super
- isInstanceOf, asInstanceOf, classOf
- protected variable cannot be visited by classes or objects under the same package. it can only be accessed from subclass. protected[this]
- only primary constructor can call super class constructor
class Employee(name: String, age: Int, val salary : Double) extends Person(name, age)
- override field/method
class Person(val name: String) {
override def toString = s"${getClass.getName}[name=$name]"
}
class SecretAgent(codename: String) extends Person(codename) {
override val name = "secret" // val override field
override val toString = "secret" //val override method
}
abstract class Person { // See Section 8.8 for abstract classes
def id: Int // Each person has an ID that is computed in some way
...
}
class Student(val id: Int) extends Person
// A student ID is simply provided in the constructor
• A def can only override another def.• A val can only override another val or a parameterless def.
• A var can only override an abstract var
- anonymous subclass
val alien = new Person("Fred") {
def greeting = "Greetings, Earthling! My name is Fred."
}
alien is object of structural type, Person{def greeting: String}
def meet(p: Person{def greeting: String}) {
println(s"${p.name} says: ${p.greeting}")
}
- Construction Order
class Creature {
val range: Int = 10
val env: Array[Int] = new Array[Int](range) // range is getter method
}
class Ant extends Creature {
override val range = 2
}
problem is env will be a empty array when new object of Ant due to range is overridden in Ant.
Thus, you should not rely on the value of a val in the body of a constructor.
Remedy is early definition.
class Ant extends { override val range = 2 } with Creature
- inheritance hierarchy
Any -> isInstanceOf, asInstanceOf, equal and hashcode, ==(final), !=(final), toString, ##
AnyRef -> wait, notify/notifyAll, synchronized , eq (reference equality)
Null has sole instance null which can only be set to AnyRef classes
Nothing has no instance. used in generic construct, like Nil has type List[Nothing]
not implemented method with type Nothing
class Person(val name: String) {
def description = ???
}
def error(message: String): Nothing = //error method can be used as return of any return type
throw new RuntimeException(message)
Unit has sole value ().
def printAny(x: Any) { println(x) }
def printUnit(x: Unit) { println(x) }
printAny("Hello") // Prints Hello
printUnit("Hello")
// Replaces "Hello" with () and calls printUnit(()), which prints ()
def show(o: Any) { println(s"${o.getClass}: $o") } //Any or AnyRef
show(3) // Prints class java.lang.Integer: 3
show(3, 4, 5) // Prints class scala.Tuple3: (3,4,5)
- Object Equality
default equals calls eq to compare references.
final override def equals(other: Any) = {//parameter is Any, change hashcode too.
other.isInstanceOf[Item] && {
val that = other.asInstanceOf[Item]
description == that.description && price == that.price
}
}
final override def hashCode = (description, price).## //combine hashcodes of the two
== calls equals method for reference type.
- Value classe
2. Its primary constructor has exactly one parameter, which is a val, and no body.
3. The class has no other fields or constructors.
4. The automatically provided equals and hashCode methods compare and hash the
underlying value.
class MilTime(val time: Int) extends AnyVal {
def minutes = time % 100
def hours = time / 100
override def toString = f"$time04d"
}