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_.yourpackagepath

chained 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
1. The class extends AnyVal.
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"
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章