如果類沒有內(nèi)容(body),可以省略空的花括號(hào),只寫一行語(yǔ)句
class User
val user1 = new User
類可以帶參數(shù),類名稱C之后圓括號(hào)內(nèi)的參數(shù),x稱為類參數(shù)class parameter,Scala編譯器會(huì)使用類參數(shù)創(chuàng)建出一個(gè)主構(gòu)造函數(shù)paimary constructor
class C(x: R)
私有主構(gòu)造函數(shù),只在被類本身以及伴生對(duì)象訪問
class class C(private x: R)
Scala編譯器會(huì)將編譯類內(nèi)部的任何代碼,如果代碼不是字段定義和方法定義,則被編譯到主構(gòu)造函數(shù)中
可以用_對(duì)成員初始變量進(jìn)行初始化,對(duì)應(yīng)為0或者null
class C(var x: R) {
assert(x > 0, "positive please") //對(duì)參數(shù)的合法性進(jìn)行檢驗(yàn)
var y = x
val readonly = 5
private var secret = _
def this() = this(42)
}
assert 方法位于
scala.Predef對(duì)象內(nèi)
輔助構(gòu)造函數(shù)
主構(gòu)造函數(shù)之外的構(gòu)造函數(shù)稱為輔助構(gòu)造函數(shù),輔助構(gòu)造函數(shù)的第一個(gè)條語(yǔ)句必須是調(diào)用同類的其他構(gòu)造函數(shù),所有每個(gè)Scala構(gòu)造器最終都會(huì)調(diào)用主構(gòu)造器
def this() = this(42)
私有構(gòu)造函數(shù)
在類名稱和參數(shù)列表之間添加關(guān)鍵字private/protected,來聲明私有的或受保護(hù)的構(gòu)造函數(shù)
class C private (var x: R)
隱式轉(zhuǎn)換
implicit 修飾符告訴編譯器在某些情況下自動(dòng)應(yīng)用它,它必須定義在作用域范圍之內(nèi)
implicit def intToRational(x: Int) = new Rational(x)
創(chuàng)建一個(gè)隱式轉(zhuǎn)換,在需要時(shí)自動(dòng)將整數(shù)轉(zhuǎn)換為有理數(shù)
抽象類
使用abstract來聲明
abstract class D { ... }
繼承
子類擁有父類非private的方法,如果不顯示指明繼承,默認(rèn)繼承scala.AnyRef,與java.lang.Object等同
class C extends D { ... }
方法重載
override關(guān)鍵詞,進(jìn)行函數(shù)重載,如果子類重寫了父類的具體函數(shù),必須添加override修飾符,否則就會(huì)出現(xiàn)編譯錯(cuò)誤
在Scala中,字段和方法屬于同一個(gè)名稱空間,可以使用字段覆蓋無(wú)參數(shù)的方法
abstract class Element {
def contents: Array[String]
}
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
Java有四個(gè)名稱空間是字段,方法,類型和包,相比之下,Scala有兩個(gè)命名空間namespace:
- 值(字段,方法,包和單例對(duì)象)
- 類型(類和特質(zhì)名稱)
因此可以用字段覆蓋無(wú)參數(shù)的方法,同一類內(nèi)部的字段和方法名稱不能相同
調(diào)用超類的構(gòu)造函數(shù)
傳遞參數(shù)給超類的主構(gòu)造函數(shù),把參數(shù)放到超類名稱后的括號(hào)內(nèi)即可
class D(var x: R)
class C(x: R) extends D(x)
參數(shù)化字段 parametric field
一種簡(jiǎn)寫方式,用于組合參數(shù)和字段,同時(shí)定義同名的參數(shù)和字段
//這里的conts只是構(gòu)造函數(shù)的參數(shù),類沒有對(duì)應(yīng)的字段
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
// 簡(jiǎn)寫,自動(dòng)生成同名的字段
class ArrayElement(
val contents: Array[String]
) extends Element
參數(shù)前可以是val,var,private,protected,overried之類的修飾符
class Cat {
val dangerous = false
}
class Tiger(
override val dangerous: Boolean,
private var age: Int
) extends Cat
//等價(jià)于
class Tiger(param1: Boolean, param2: Int) extends Cat {
override val dangerous = param1
private var age = param2
}
實(shí)際上如在出構(gòu)造函數(shù)之外的地方使用構(gòu)造參數(shù),內(nèi)部依然會(huì)把它轉(zhuǎn)換為private val類型的字段
class Foo(bar: Int){
def funtion1(): Unit ={
println(bar)
}
}
內(nèi)部包含字段private val bar: Int,與class Foo(private val bar: Int)的區(qū)別在于沒有對(duì)應(yīng)的私有g(shù)etter方法private int bar();生成
變量的Getter/Setter方法
Scal字段會(huì)自動(dòng)生成getter和setter方法,val類型沒有setter方法
Scala支持setter方法的名稱為getter函數(shù)名后加_=,使用時(shí)可以對(duì)getter函數(shù)名直接賦值
class Test{
private var _x = 0
def x = _x //get方法
def x_= (newValue: Int): Unit = { //set方法
_x = newValue
}
}
//使用方法
val test = new Test
test.x = 5
val x = test.x
Singleton Objects
Scala沒有static成員,通過單例對(duì)象來實(shí)現(xiàn)類似功能,等同于把所有的靜態(tài)成員都放到了單例對(duì)象中,可以通過名稱直接調(diào)用它的方法,就像一個(gè)靜態(tài)方法的集合
- 它的定義類似于一個(gè)類的定義,不同的是它的關(guān)鍵字是
object - object是只有一個(gè)實(shí)例的類,本身可以看做是一個(gè)惰性加載的
val類型 - 實(shí)際實(shí)現(xiàn)是一個(gè)合成類(synthetic class)實(shí)例,被類成員中的一個(gè)靜態(tài)變量引用,編譯產(chǎn)生的類名是對(duì)象名稱加上美元符號(hào)
SingletonObjectName$
單例對(duì)象和類的一個(gè)不同在于單例對(duì)象不能接收參數(shù),而類可以。因?yàn)椴荒苁褂?new 來實(shí)例化一個(gè)單例對(duì)象,因此沒有辦法給它傳遞參數(shù)
Companion object
單例對(duì)象的名字和類名稱一致的時(shí)候叫做伴生對(duì)象companion object,必須在同一個(gè)文件中定義類和它的伴生對(duì)象,他們可以互相訪問對(duì)方的私有成員
standalone object
沒有和類共享名稱的單例對(duì)象叫做獨(dú)立對(duì)象,為了運(yùn)行一個(gè)Scala程序,必須提供一個(gè)獨(dú)立對(duì)象,內(nèi)部包含一個(gè)程序執(zhí)行的入口函數(shù)def main(args: String) = {}
Scala每個(gè)源文件都默認(rèn)包含
java.lang和scala包,以及scala.Predef的單例對(duì)象
Predef中包含了許多有用的方法, 例如,println和assert方法
同時(shí)Scala不要求.scala文件名稱對(duì)應(yīng)內(nèi)部的類,可以隨意命名
通過混入scala.App類型的trait,定義應(yīng)用程序?qū)ο?,即在?duì)象后添加extends App,放置在main方法中的代碼直接放在單體對(duì)象的花括號(hào)之間,通過args字符串?dāng)?shù)組訪問命令行參數(shù)即可
特質(zhì)trait
封裝字段和方法定義,類似Java的接口,使用extends或者with關(guān)鍵字,混入(mixin)到類中,數(shù)量沒有限制
使用extends關(guān)鍵詞,類隱式繼承特質(zhì)的超類(特質(zhì)沒有聲明超類時(shí),默認(rèn)為AnyRef)
如果類已經(jīng)顯示擴(kuò)展超類,使用with
- trait不能有類參數(shù)(傳遞到類的主構(gòu)造函數(shù)的參數(shù))
- 超類調(diào)用是不確定的,動(dòng)態(tài)變化的(trait超類的調(diào)用方式)
trait T1; trait T2
class C extends T1 with T2
class C extends D with T1 with T2
特質(zhì)不僅可以有抽象方法,也可以包含具體方法
trait Ordered[A] extends Any with java.lang.Comparable[A] {
def compare(that: A): Int
def < (that: A): Boolean = (this compare that) < 0
def > (that: A): Boolean = (this compare that) > 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
def compareTo(that: A): Int = compare(that)
def compareTo(that: A): Int = compare(that)
}
stackable modification 可堆疊的變化
特點(diǎn)
- 特質(zhì)指定超類,那么只能用于同樣擴(kuò)展該超類的類
- 特質(zhì)的抽象方法調(diào)用超類,特質(zhì)中的super調(diào)用是動(dòng)態(tài)綁定的,混入另一個(gè)特征或類后,才有具體的定義
abstract override修飾符組合,只能由于特質(zhì),它表明特質(zhì)必須混入到有具體方法定義的類中
abstract class IntQueue {
def get(): Int
def put(x: Int)
}
import scala.collection.mutable.ArrayBuffer
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x: Int) = { buf += x }
}
trait Doubling extends IntQueue {
abstract override def put(x: Int) = { super.put(2 * x) }
}
trait Incrementing extends IntQueue {
abstract override def put(x: Int) = { super.put(x + 1) }
}
trait Filtering extends IntQueue {
abstract override def put(x: Int) = {
if (x >= 0) super.put(x)
}
}
val queue = (new BasicIntQueue with Filtering with Incrementing)
多個(gè)traits重載同一個(gè)方法,不同的次序會(huì)產(chǎn)生不同的類,優(yōu)先級(jí)是從右向左,以類似棧的形式調(diào)用函數(shù)
類似多重繼承,是利用線性化linearization算法解釋超類
當(dāng)用new實(shí)例化一個(gè)類時(shí),Scala把這個(gè)類及其所有繼承的類和特質(zhì)以線性次序放在一起。 然后,每當(dāng)其中一個(gè)類中調(diào)用super時(shí),被調(diào)用的方法就是鏈中的下一個(gè)。 如果除最后一次調(diào)用之外的所有方法都調(diào)用super,那么最終結(jié)果就是是可堆疊行為