類是對(duì)象的藍(lán)本艺沼,Scala中的類和Java中的類的很多概念是相似的奶栖,但又有些區(qū)別盐捷。
類定義
以下是一個(gè)類的簡(jiǎn)單定義
class Student {
// 使用var關(guān)鍵字定義字段
private var name = ""
private var birthDate = new Date()
/**
* 定義一個(gè)方法,返回屬性值
*/
def getName() = name
/**
* 定義方法辜腺,執(zhí)行一些具有副作用的操作
*/
def sayHi(): Unit = {
println("hi")
}
}
object App {
def main(args: Array[String]): Unit = {
// 使用new關(guān)鍵字構(gòu)建一個(gè)新的實(shí)例
val student = new Student()
// 調(diào)用實(shí)例的方法
student.getName()
}
}
使用class關(guān)鍵字定義一個(gè)類休建;
使用var/val定義類的字段,使用var定義的字段可以被重新賦值评疗,使用val定義的字段不可以被重新賦值
使用def function 的方式定義類的方法测砂。
在應(yīng)用中,使用new關(guān)鍵字構(gòu)造一個(gè)類的新實(shí)例百匆。
這一切和Java如此相似砌些,除了關(guān)鍵字不同。
關(guān)于構(gòu)造函數(shù)
每個(gè)scala類都有一個(gè)主構(gòu)造函數(shù)加匈,這個(gè)函數(shù)不會(huì)顯示的聲明存璃,而是混合在類的定義代碼中,也就是類中所有的語句都是主構(gòu)造函數(shù)矩动。
而輔助構(gòu)造函數(shù)則是以this命名的函數(shù)(這和Java不一樣有巧。之所以使用this命名,是為了方便類的重構(gòu)悲没,在重命名一個(gè)類時(shí),只需要修改類的名字男图,而不需修改所有的輔助構(gòu)造函數(shù))示姿。
以下是給Student類添加了輔助構(gòu)造函數(shù)
class Student {
// 使用var或者val字段
private var name = ""
......
// 定義一個(gè)輔助構(gòu)造函數(shù)
def this(str: String) {
// 輔助構(gòu)造函數(shù)必須顯示調(diào)用主構(gòu)造函數(shù)一次
this()
// 執(zhí)行其它的操作
this.name = str
}
}
次要構(gòu)造函數(shù)也通過new關(guān)鍵字調(diào)用。
樣例類
我們可以通過case class 定義一個(gè)樣例類
樣例類會(huì)為我們添加一些特性逊笆,
- 添加一個(gè)跟類同名的工廠方法栈戳,工程方法使得我們可以通過像調(diào)用一個(gè)函數(shù)一樣構(gòu)建一個(gè)新的對(duì)象
- 參數(shù)列表中的每個(gè)屬性都會(huì)獲得一個(gè)val前綴,所有的參數(shù)都會(huì)被當(dāng)做類的字段來處理
- 編譯器會(huì)幫助我們實(shí)現(xiàn)toString,hashCode,equalse等方法
- 編譯器會(huì)添加一個(gè)copy方法用于制作修改過的拷貝难裆。
以下是修改過的Student類子檀,并展示了它的一些特性
/**
* 使用case class定義一個(gè)類
*/
case class Student(name: String,
birthDate: Date,
address: String) {
def sayHi(): Unit = {
println("hi")
}
}
object App {
def main(args: Array[String]): Unit = {
// 使用new 方法來構(gòu)造對(duì)象
val student = new Student("張三", new Date(), "北京")
// 使用和類同名的工廠方法來實(shí)例化一個(gè)對(duì)象
val student2 = Student("張三", new Date(), "北京")
// 參數(shù)被當(dāng)中字段處理
println(student.name)
// 使用copy方法,復(fù)制一個(gè)新的對(duì)乃戈,
// copy方法的參數(shù)是具有默認(rèn)值的褂痰,因此可以只賦值某些參數(shù)
val student3 = student.copy(name = "王五")
// 調(diào)用對(duì)象的方法,和普通對(duì)象行為一致
student3.sayHi()
}
}
我們可以看到我們使用了非常上的代價(jià)症虑,獲得了一些很酷的特性缩歪,比如java中的大量的get,set方法,hashCode和equasle方法谍憔,定義工程方法等匪蝙。(在java中也可以通過lombok之類的庫實(shí)現(xiàn)這些功能主籍,但行為并不完全一致)
對(duì)象
scala比java更面向?qū)ο笠稽c(diǎn),它沒有類方法逛球。而是使用單例對(duì)象來實(shí)現(xiàn)跟java類方法相同的功能千元。比如前面示例中的App對(duì)象。
單例對(duì)象的定義與類非常相似颤绕,區(qū)別在于使用object而不是class關(guān)鍵字诅炉。但又有一些區(qū)別。
- 直接使用object.xxx的方式訪問對(duì)象的屬性或者方法
- 單例對(duì)象不能接受參數(shù)
- 在運(yùn)行時(shí)屋厘,單例對(duì)象的實(shí)例只有一個(gè)涕烧,如果在某個(gè)地方修改了單例對(duì)象某個(gè)屬性,其它引用該單例對(duì)象的屬性也會(huì)相應(yīng)變化汗洒。
伴生對(duì)象
和某個(gè)類在同一個(gè)文件中定義议纯,并且與類同名的對(duì)象被稱作類的伴生對(duì)象。
類和它的伴生對(duì)象可以互相訪問對(duì)方的私有成員溢谤。
工廠方法
單例對(duì)象中有個(gè)特殊的方法apply瞻凤。當(dāng)我們?cè)趩卫龑?duì)象中定義某個(gè)方法為apply時(shí),我們可以直接通過 object(...)的方式來調(diào)用apply方法世杀。這看起來就像調(diào)用一個(gè)函數(shù)一樣阀参。它通常和它的伴生類一起使用,完成復(fù)雜的對(duì)象初始化過程瞻坝。
我們給上面的Student增加一個(gè)伴生對(duì)象蛛壳,以便完成更加復(fù)雜的初始化工作。
case class Student(name: String,
birthDate: Date,
address: String) {
def sayHi(): Unit = {
println("hi")
}
}
object Student {
// 定義一個(gè)名為apply的方法
def apply(name: String): Student = {
// ... 可以在這之前實(shí)現(xiàn)一些復(fù)雜的計(jì)算保證最終返回Student的一個(gè)實(shí)例即可
Student(name, new Date(), "北京")
}
def apply(name: String, birthDate: Date): Student = {
Student(name, birthDate, "北京")
}
}
object App {
def main(args: Array[String]): Unit = {
// 使用工廠方法生成新的對(duì)象所刀,看起來就像一個(gè)函數(shù)調(diào)用
val st = Student("張三", new Date())
st.sayHi()
}
}
關(guān)于Scala的類和對(duì)象還有很多細(xì)節(jié)衙荐。