跳至主要内容

Scala 中的類別

Scala 的 Class 種類

Type Key wordInstantiation
Class就是 instance 的 templateclass new
Case Class類似 enum 的 instancecase class 不須 new
Companion Class當一般的 class 有 Companion Object 時,該類稱之
Companion Object類似 Singleton, Static classobject class搭配 apply methods

Scala 的 Class 結構

Scala 的 Companion Object

  • 是一個 Singleton
  • 一定會有一個同名的 Companion Class
  • Companion Class 與 Companion Object 的 private fields 可以互通
  • Companion Class 與 Companion Object 間 推測應該是被 Compile 成一個 class 與 inner static class 間的關係
  • Scala 中沒有靜態修飾符, 但是 Companion Object 下的成員都是 static

Scala Default Constructor

  • 點運算的同義 method _$eq()
  • constructor parameters 的存取限制
    • var/val 決定是否建立 getter/setter
    • var/val 可以搭配 private 使用, => 不建立 getter/setter
    • 若 constructor param 未加上 var/val 一律視為加上 private. Scala 不會生成 get/setters
  • Auxiliary Constructor: 輔助建構子
    • 方法名為 this 的 method, 且以 this 呼叫其他建構子
    class Person(var firstName: String, var lastName: String, nickname: String) {
println("the constructor begins")
// some class fields
private val HOME = System.getProperty("user.home")
var age = 0

// some methods
override def toString = s"$firstName $lastName is $age years old"

def printHome {
println(s"HOME = $HOME")
}

def printFullName {
println(this)
} // uses toString

def anotherName {
println(nickname)
}
printHome
printFullName
println("still in the constructor")

}
  • Scala mutator (setter) 的同義方法 _$eq(value)
    var p = new Person("Michael", "Jackson")

p.firstName ="John"
p.firstName_$eq("Mary")

Scala 子類的建構子

  • 子類建構子參數的存取
    • 子類新建的 parameters 仍需定義 var/val
    • Constructor parameter 的 var/val 關鍵字 以及他的型別宣告在第一次出現處
    • 子類新的 params 不可以與 父類 params 同名.(不然 自動生成的 get/setter 不就錯亂了嗎)
    • 子類 extends 後方不須再定義型別, 因為父類已經定義過
  • Auxiliary Constructor 繼承父類別
    class Book(val title: String) {  }
//不需要 title: String
class Magazine(val ISBN: String, title: String) extends Book(title) { }

val m = new Magazine("1234", "SPORTS")

Scala private Constructor/Fields

  • 注意 private 寫在 constructor code block 前
  • ex: singleton example
  • Object-private fields
    // 注意 private 寫在 code block 前
class Brain private {
override def toString = "This is the brain."

//public
var think: Double = 0.0d;

private var talk: Double = 0.0d;


}
object Brain {
val brain = new Brain
def getInstance = brain
}

val brain = Brain.getInstance

Scala Object-Private Fields

  • 關鍵字: 在變數定義前加上 private[this]
  • 限制只能在 scala object 中使用變數
  • 加上 private[this] 會變成類似 private static variable, 其他 instance 也無法存取
    • Companion Class/Object 中的 private fields 預設是可以相互存取的
  • 但是初始化 static variable 須經由 companion Object
   class Stock {
// a private[this] var is object-private, and can only be seen
// by the current instance
private[this] var price: Double = _

def setPrice(p: Double) { price = p }

// error: this method won't compile because price is now object-private
def isHigher(that: Stock): Boolean = this.price > that.price
}

Scala Auxiliary Constructor (輔助建構子)

  • Scala 中有 named-param, default-param, 所以減少建構子的數量
  • Auxiliary Constructors 指的是 Java 中,以 this 呼叫其他建構子的技巧
  • this 為 method name 的其他 methods 稱 Auxiliary Constructors.
    • example 1: default Constructor params 最多
    • example 2: default Constructor params 最簡化
    • Auxiliary Constructors 經由 this delegate default constructor
       //default value: 加上 =
def log(message: String, level: String = "INFO") = {
println(s"$level: $message")
}


//example1: primary constructor 最複雜
class Pizza(var crustSize: Int, var crustType: String) {
// one-arg auxiliary constructor
def this(crustSize: Int) {
this(crustSize, Pizza.DEFAULT_CRUST_TYPE)
}

// one-arg auxiliary constructor
def this(crustType: String) {
this(Pizza.DEFAULT_CRUST_SIZE, crustType)
}

// zero-arg auxiliary constructor
def this() {
this(Pizza.DEFAULT_CRUST_SIZE, Pizza.DEFAULT_CRUST_TYPE)
}

override def toString = s"A $crustSize inch pizza with a $crustType crust"
}


//example2: default constructor 最簡化
class Pizza2() {
var crustSize = 0
var crustType = ""

def this(crustSize: Int) {
this()
this.crustSize = crustSize
}

def this(crustType: String) {
this()
this.crustType = crustType
}
}
  • Auxiliary Constructor vs. Companion Apply (case class 的 multiple constructors)
    • AuxCon 簡化 invoker 使用, 減少所需傳入的參數
    • Case class 搭配 companion object 時不須 new 關鍵字
    • 一個 companion object 可有多個 apply methods
  • Auxiliary Constructor 使用 this methods
  • Companion Object 使用 apply methods
    // Scala Companion apply methods (case class apply methods)
// the case class
case class PersonC (var name: String, var age: Int)

// the companion object
object PersonC {
def apply() = new PersonC("<no name>", 0)
def apply(name: String) = new PersonC(name, 0)
}

val a = PersonC() // corresponds to apply()
val b = PersonC("Pam") // corresponds to apply(name: String)
val c = PersonC("William Shatner", 82)

複寫預設的 Scala Getter/Setter 技巧

  • 可達到利用點運算取直與設值的功能
    • a.name / a.name="new name"
    • Scala style 的語法, 但是 name 只能定義一次的限制
  • Conventions
    • 先定義 Accessor
    • Mutator 需命名為 Accessor Name 加一個底線
    • Mutator 需要以 函數變數型態定義. ([參考上方 Scala function 章節])
  • 函數變數: 因為是變數, 所以等號移到變數之後, 而非實作區塊前
      // 這邊以 private 封裝原始的名稱, 若未封裝則可以用 person._original 存取
class Person(private var _original: String) {
def name = _name //accessor
def name_= (input: String) { _original= input } // mutator
}

var person = new Person(_original= "totem")
person.name

person.name ="Totem"

Scala Lazy Fields (Performance)

  • 如果一個變數值需要大量運算, 可採用 lazy field 的技巧
  • 需使用 lazy 關鍵字
  • 將 Code Block or Function 指定給一個變數
  • 範例中的 text 是檔案的暫存區
  • 可以與 function 比較(fun: 回傳值是每次呼叫重新計算一次)
    // lazy field example
class Foo {

import scala.xml.XML
// 這裡有有 lazy 關鍵字
lazy val text =
io.Source.fromFile("/etc/passwd").getLines.foreach(println)
}

// 這邊沒加 lazy 關鍵字, 在 Foo 建構時, 便會讀取 file 中的資料
class Foo {
val text = {
var lines = ""
try {
lines = io.Source.fromFile("/etc/passwd").getLines.mkString
} catch {
case e: Exception => lines = "Error happened"
}
lines
}
println(text)
}

Scala 沒初始值 fields 定義

  • 改用 [Option] 來處理 None / Some
  • 範例中的 Person.address 預設是 None
   case class Person(var username: String, var password: String) {
var age = 0
var firstName = ""
var lastName = ""
var address = None: Option[Address]
}

case class Address(city: String, state: String, zip: String)

//給值
val p = Person("alvinalexander", "secret")
p.address = Some(Address("Talkeetna", "AK", "99676"))

Abstract class/Abstract Fields/Trait 下如何建立屬性(Properties)

  • 只有 abstract class 可以建立 abstract fields (greeting/age)
  • abstract fields 在 concrete class 中實作時要再 需告 var/val
    • abstract fields(限定子類要實作), Scala compiler 只會幫忙建立 get/set methods.
    • Java 中沒有 Abstract Field 概念
    // 注意這是一個 abstract class
// Cons_params 預設是 var
abstract class Pet (name: String) {
val greeting: String // greeting and age 都是 abstract fields
var age: Int
def sayHello { println(greeting) }
override def toString = s"I say $greeting, and I'm $age"
}

// 實作的 concrete class 的 field 仍需定義 val/var
class Dog (name: String) extends Pet (name) {
val greeting = "Woof"
var age = 2
}
class Cat (name: String) extends Pet (name) {
val greeting = "Meow"
var age = 5
}

Scala 物件全等判斷

  • Object Equality (Equals and Hashcode),JVM 語言基本上 概念同 Java
  • ==, equals, eq
  • Scala 中 equals / hashcode methods
    class Person (name: String, age: Int) {
def canEqual(a: Any) = a.isInstanceOf[Person]
override def equals(that: Any): Boolean =
that match {
case that: Person => that.canEqual(this) && this.hashCode == that.hashCode
case _ => false
}
override def hashCode:Int = {
val prime = 31
var result = 1
result = prime * result + age;
result = prime * result + (if (name == null) 0 else name.hashCode)
return result
}
}

Scala Inner Class

  • Scala 中實現 Inner class 的方式
    • 在 outer class scope 中定義一個 Case class
  • 先定義 Inner class 的目的
    • 想建立一個被封裝在另一個 Class 之內的 Class
    • 因為想封裝特定的程式碼
    • 讓 Inner class 與 public API 去耦合
    • 與外層綁定的 Class
    class PandorasBox {

case class Thing(name: String)

var things = new collection.mutable.ArrayBuffer[Thing]()
things += Thing("Evil Thing #1")
things += Thing("Evil Thing #2")

def addThing(name: String) {
things += new Thing(name)
}
}


//使用時看不出 things 的存在
val p = new PandorasBox
p.things.foreach(println)

Scala 的 Methods

  • Scopes of Scala Methods
TypeScopeSyntaxDesc
object-private scope
companion object
the current instance of the current object
只有當前這一個 instance 可以使用private[this] def isFoo = true可終止 Companion Class/Object 的 private fields 可以互通關係
甚至限制 equals 比對
private與 Java 同概念private def isFoo = true
protected允許子類呼叫
注意 不同於 Java
Java 的 protected 可允許同 package 下其他 class 存取(Java較寬鬆)
protected def breath { }
package-specific限定只能在特定 package 內使用(包含 subpackages)
指定 package 下的成員都可使用
private packageName def isBar = true
public不須加 access modifier
    //Object private
class Foo {
// object private method
private[this] def isFoo = true

def doFoo(other: Foo) {
// // isFoo 限定 this 才能呼叫, 所以 this line won't compile
if (other.isFoo) {
}
}
}


//package private
package com.acme.coolapp.model {
class Foo {

//com.acme.coolapp.model._ 內都可以使用
private[model] def doX {}
//com.acme.coolapp._ 內都可以使用
private[coolapp] def doY {}
//com.acme._ 內都可以使用
private[acme] def doZ {}

//只有 Foo class 下能使用
private def doY {}

}
class Bar {
val f = new Foo
f.doX // compiles
f.doY // won't compile
}
}