八、類
類是對象的模板, 屬性(有什麼)和方法(做什麼)
類定義
package classes
// 簡化寫法
// class User
// 標準寫法
class User {
}
object UserDemo {
def main(args: Array[String]): Unit = {
// 創建對象 "new" 關鍵字
val user1 = new User() // 調用類提供的默認無參構造
val user2 = new User
}
}
關鍵字
new
被用於創建類的實例,類默認提供一個無參的構造器
簡單類
Scala中的類,類似於java,由屬性和方法構成
package classes
class Student {
var name: String = "" // 必須初始化
var age: Int = 0
def sayHello(): String = {
"Hello:" + name + ",I am " + age + "years old"
}
// override def toString: String = this.name + "\t" + this.age
}
object StudentDemo {
def main(args: Array[String]): Unit = {
val s1 = new Student()
s1.name = "小紅"
s1.age = 18
val s2 = new Student
s2.name = "小青"
s2.age = 20
println(s1) // 小紅 18
println(s2) //小青 20
}
}
getter/setter
自動生成
class Person {
var name: String = null
val age : Int = 0
private var sex: Boolean = false
}
注意:
- 在scala類中,聲明的屬性都是私有成員
- 用
var
定義的屬性,自動提供getter/setter方法- 用
val
定義的屬性,自動提供getter方法- scala中的getter/setter,命名不同於java,語法是 name 和 name_
- 在scala中屬性一旦設置爲
private
, 它的getter/setter方法均爲私有,僅限本類使用。
手動生成
getter方法 ----> def 區分成員名() = 成員值
setter方法 ----> def 區分成員名_(形參) {成員值 = 形參值}
package classes
class Dog {
private var privateColor: String = "black"
// 屬性名和方法名不能相同
// def color(): String = privateColor
// 手動生成getter/setter方法
def color = privateColor
def color_(color: String): Unit = {
privateColor = color
}
}
object DogDemo {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.color_("red")
println(dog.color)
}
}
@BeanProperty
import scala.beans.BeanProperty
class Fish {
// 自動生成scala和java風格的getter和setter
@BeanProperty var kind:String = ""
}
object Fish{
def main(args: Array[String]): Unit = {
val fish = new Fish
fish.kind = "金魚"
println(fish.kind)
val fish2 = new Fish
fish2.setKind("鯽魚")
println(fish2.getKind)
}
}
注意:scala的字段被標註爲@BeanProperty時,會自動生成Java和Scala的getter/setter
反編譯後的結果如下:
public class Person3 { private String name = null; public String name() { return this.name; } public void name_$eq(String x$1) { this.name = x$1; } public String getName() { return name(); } public void setName(String x$1) { name_$eq(x$1); } }
輔助構造器
類似於Java中重載的構造方法,Scala的類有一個主構造器(Primary Constructor)和任意多個輔助構造器(Auxiliary Constructor)
- 輔助構造器的名稱爲this
- 每一個輔助構造器必須以一個對先前已定義的其它輔助構造器或主構造器的調用開始
如:
package classes
class Fish {
var kind: String = ""
var name: String = ""
def this(name: String) {
this() // 調用主構造
this.name = name
}
def this(name: String, kind: String) {
this(name) // 調用前一個輔助構造器
this.kind = kind
}
}
object FishDemo{
def main(args: Array[String]): Unit = {
val f1 = new Fish("鯽魚")
val f2 = new Fish("金魚","xdd")
val f3 = new Fish()
}
}
主構造器
在scala中,每個類都有主構造器。主構造器並不以this方法定義,而是與類定義交織在一起
// 主構造器的參數寫在類名之後 方法體爲整個類
class Tiger(val color: String){
}
主構造的參數被編譯成字段,其值被初始化成構造傳入的參數。注意:主構造函數看上去和類的定義已經完全融合在了一起!它的參數列表放到了類名的後面(我們也可以直接叫它類參數列表),它的方法體就是整個類體,實例化一個類時,類體(主構造函數)中所有可行的部分都會被執行,不管是函數調用還是表達式等等,只是對於類的字段和方法聲明而言是沒有什麼可執行的,它們只是聲明而已。
// 主構造器的參數寫在類名之後 color
class Tiger(val color: String) {
println("構造開始")
def sayHi(name: String): String = {
"Hello:" + name
}
println("構造結束")
}
object TigerDemo {
def main(args: Array[String]): Unit = {
val t1 = new Tiger("紅色")
println("-------------------------------")
val t2 = new Tiger("白色")
}
}
/*
構造開始
構造結束
-------------------------------
構造開始
構造結束
*/
主構造的其它變化
// 對於var修飾的參數:外部可讀/可改寫 (實際上是:編譯器爲該類參數(字段)自動生成了getter和setter)
class Tiger2(var name: String, var age: Int) {
var sex: String = ""
}
// 同上
class Tiger2(var name: String, var age: Int) {
var sex: String = ""
}
// 對於val修飾的參數:外部可讀/不可改寫(實際上是:編譯器爲該類參數(字段)只生成了getter沒有生成setter)
class Tiger3(var name: String, var age: Int, val sex: Boolean) {
}
// 對於private var修飾的參數:內部可讀/可改寫 (編譯器不會爲私有類參數(字段)自動生成getter和setter)
// 對於private val修飾的參數:內部可讀/不可改寫 (編譯器不會爲該類參數(字段)自動生成getter和setter)
class Tiger4(private var name: String) {
}
// 不帶val或者var的參數,不會作爲類的成員,只有被一個方法所使用,它將被升格爲字段
class Tiger5(name: String, age: Int) {
def sayHi() = "Hello:" + name
}
八、對象
單例對象
Scala中沒有靜態方法或者靜態屬性,可以使用object
這樣的語法達到相同目的。
package objects
object IdFactory {
private var id: Int = 0
def getId(): Int = {
id += 1
id
}
}
object TestIdFactory {
def main(args: Array[String]): Unit = {
println(IdFactory.getId()) // 1
println(IdFactory.getId()) // 2
println(IdFactory == IdFactory) // true
}
}
伴生對象
當類名和單例類名字一樣的時候,我們把單例類稱爲伴生對象
package objects
// 伴生對象
object Person {
private var id: Int = 0
def getPersonNum(): Int = {
id += 1
id
}
def main(args: Array[String]): Unit = {
val p1 = new Person()
val p2 = new Person()
val p3 = Person
val p4 = Person
println(p1 == p2) // false
println(p3 == p4) // true
println(p1.id) // 1
println(p2.id) // 2
}
}
// 伴生類
class Person {
private var id: Int = Person.getPersonNum() // 伴生類中 可以調用伴生對象的私有方法
private var address: String = ""
}
注意:
- object聲明的爲伴生對象,class聲明的爲伴生類
- 伴生類可以訪問伴生對象中的私有成員,前提是在同一源文件中,語法:伴生對象.私有成員|方法
apply & unapply
apply 方法用在object中一般作爲工廠方法用於產生Class對象
class Tiger(var kind: String) {}
object Tiger {
def apply(kind: String): Tiger = new Tiger( kind)
def main(args: Array[String]): Unit = {
val t1= Tiger.apply("東北虎")
val t2 = Tiger("東北虎")
}
}
apply方法的最佳實踐方式之一就是用來做工廠。比如在Scala的標準庫中,許多集合類給我們提供了apply方法來創建集合:
val a1 = Array(1,2,3) val b1 = List("Hello Hadoop","Hello Scala")
可以認爲unapply方法是apply方法的反向操作,apply方法接受構造參數變成對象,而unapply方法接受一個對象,從中提取值
class Tiger(var kind: String) {}
object Tiger {
def apply(kind: String): Tiger = new Tiger(kind)
def unapply(arg: Tiger): Option[String] = {
if (arg == null) None
else Some(arg.kind)
}
def main(args: Array[String]): Unit = {
val t1 = Tiger.apply("東北虎")
val t2 = Tiger("東北虎")
val Tiger(kind) = t2
println(kind) // 東北虎
}
}