類和對(duì)象
Scala是一個(gè)函數(shù)式面向?qū)ο笳Z言
什么是面向?qū)ο螅?/p>
面向?qū)ο笫且环N變成語言,它是基于面向過程的,強(qiáng)調(diào)的是以對(duì)象為基礎(chǔ)完成各種操作
三大特點(diǎn):
- 跟符合程序員的思考習(xí)慣
- 把復(fù)雜的事情簡單化
- 把程序員從執(zhí)行之變?yōu)橹笓]者
什么是類?
類是屬性和行為的集合,是一個(gè)抽象的概念,看不見也摸不著
類和無參構(gòu)造器
在Scala中,類并不用聲明為public;Scala源文件中可以包含多個(gè)類,所有這些類都具有公有可見性;val修飾的變量(常量),值不能改變,只提供getter方法,沒有setter方法;var修飾的變量,值可以改變,對(duì)外提供getter、setter方法;如果沒有定義構(gòu)造器,類會(huì)有一個(gè)默認(rèn)的無參構(gòu)造器;
- 如果類是空的,沒有任何成員,可以省略{}
- 如果構(gòu)造器的參數(shù)為空,可以省略()
package hhb.cn.part04
/**
* @description:
* 在Scala中,類都有一個(gè)無參構(gòu)造器
* @date: 2020-09-24 10:05
**/
class Person {
//聲明字段必須進(jìn)行初始化,Scala編譯器會(huì)根據(jù)初始化值的數(shù)據(jù)類型自動(dòng)推斷字段類型,字段類型可以省略
var name = "huanghongbo"
// _ 下劃線表示一個(gè)占位符,編譯器會(huì)根據(jù)變量的數(shù)據(jù)類型賦予相應(yīng)的初始值
//使用 _ 占位符進(jìn)行賦初始值的時(shí)候,數(shù)據(jù)類型必須指定
var nickName: String = _
var age: Int = _
var numDouble: Double = _
var flag: Boolean = _
// 使用val修飾的變量不能使用占位符
// val test:Int = _
val num = 30
var a = 20
//如果對(duì)String類型賦值為null,必須指定類型,否則默認(rèn)類型為Null
var address: String = null
// 類中的私有字段,有私有的getter 和 setter方法
// 可以在類的內(nèi)部訪問,也可以被其伴生對(duì)象訪問
private var hobby = "旅游"
//對(duì)象的私有字段,訪問權(quán)限更小,只能在當(dāng)前類中訪問
private[this] var cardInfo = "10010"
def hello(message: String): Unit = {
//只能在當(dāng)前類中訪問cardInfo
println(s"$message , $cardInfo , $hobby")
}
//自定義方法實(shí)現(xiàn)兩個(gè)數(shù)據(jù)求和
def addNum(num1: Int, num2: Int): Int = {
num1 + num2
}
}
object ClassDemo {
def main(args: Array[String]): Unit = {
//使用無參構(gòu)造器創(chuàng)建對(duì)象,小括號(hào)可以省略
val person = new Person()
var p = new Person
println(s"${person.name} ${person.nickName} ${person.age} ${person.numDouble} ${person.flag}")
//給類的屬性賦值
person.age = 50
//注意:這種方式賦值其實(shí)就是調(diào)用 age_= 這個(gè)setter方法
person.age_=(20)
//這種就是調(diào)用了他的getter方法
println(person.age)
//調(diào)用類中的方法
person.hello("你好")
println(person.addNum(1, 3))
//無法調(diào)用類中的私有字段和對(duì)象的私有字段
// person.hobby
// person.cardInfo
}
}
自定義Getter和Setter方法
對(duì)于 Scala 類中的每一個(gè)屬性,編譯后會(huì)有一個(gè)私有的字段和相應(yīng)的getter、setter方法生成。如果屬性是私有的,生成的getter、setter方法也是私有的
package hhb.cn.part04
/**
* @description:
* @date: 2020-09-24 10:48
**/
class Dog {
private var _leg = 0
private var test1 = 0
var test2 = 0
//自定義getter方法
def leg: Int = _leg
//自定義setter方法
def leg_=(newLeg: Int) {
_leg = newLeg
}
}
object GetterAndSetterDemo {
def main(args: Array[String]): Unit = {
val dog = new Dog
//調(diào)用自定義的setter方法
dog.leg_=(10)
//調(diào)用自定義的getter方法
println(dog.leg)
//私有的屬性不自定義setter方法,直接使用報(bào)錯(cuò)
// dog.test1_=(1)
dog.test2_=(1)
}
}

自定義變量的getter和setter方法需要遵循以下原則:
- 字段屬性名以"
_"作為前綴,如:_leg - getter 方法定義為:
def leg = _leg - setter 方法定義為:
def leg_=(newLeg:Int)
Bean屬性
JavaBean規(guī)范把Java屬性定義為一堆getter和setter方法。類似于Java,當(dāng)將Scala字段標(biāo)注為 @BeanProperty時(shí),getFoo和setFoo方法會(huì)自動(dòng)生成。使用@BeanProperty并不會(huì)影響Scala自己自動(dòng)生成的getter和setter方法。在使用時(shí)需要導(dǎo)入包scala.beans.BeanProperty
package hhb.cn.part04
import scala.beans.BeanProperty
/**
* @description:
* @date: 2020-09-24 11:05
**/
class Teacher {
@BeanProperty var name: String = _
}
object BeanDemo {
def main(args: Array[String]): Unit = {
val teacher = new Teacher
teacher.name = "張三"
teacher.name_=("李四")
println(teacher.name)
//@BeanProperty 生成的set、get方法
teacher.setName("王五")
println(teacher.getName)
}
}
構(gòu)造器
如果沒有定義構(gòu)造器,Scala類中會(huì)有一個(gè)默認(rèn)的無參構(gòu)造器;Scala當(dāng)中類的構(gòu)造器分為兩種:主構(gòu)造器和輔助構(gòu)造器;主構(gòu)造器的定義與類的定義交織在一起,將主構(gòu)造器的參數(shù)直接放在類名之后。當(dāng)主構(gòu)造器的參數(shù)不用var或val修飾時(shí),參數(shù)會(huì)生成類的私有val成員。Scala中,所有的輔助構(gòu)造器都必須調(diào)用另外一個(gè)構(gòu)造器,另外一個(gè)構(gòu)造器可以是輔助構(gòu)造器,也可以是主構(gòu)造器。
package hhb.cn.part04
//主構(gòu)造器與類名交織在一起,類名后面的參數(shù)就是主構(gòu)造器的參數(shù)
//主構(gòu)造器直接在類中,其代碼不包含在任何方法中
class Animal(name: String, age: Int) {
//下面三行代碼也屬于主構(gòu)造器的方法
println(name)
println(age)
println("=================================")
var gender: String = ""
def this(name: String, age: Int, gender: String) {
//在類中,每個(gè)輔助構(gòu)造器必須以主構(gòu)造器或其他輔助構(gòu)造器的調(diào)用作為第一行代碼
this(name, age)
this.gender = gender
}
var color: String = ""
def this(name: String, age: Int, gender: String, color: String) {
//此處調(diào)用的是其他構(gòu)造器
this(name, age, gender)
this.color = color
}
}
object ConstructorDemo {
def main(args: Array[String]): Unit = {
val animal1 = new Animal("啦啦1", 5)
val animal2 = new Animal("啦啦2", 6,"公")
val animal3 = new Animal("啦啦3", 7,"公","紅色")
}
}
備注:每個(gè)輔助構(gòu)造器必須以主構(gòu)造器或其他輔助構(gòu)造器的調(diào)用作為第一行代碼
對(duì)象
單例對(duì)象
Scala并沒有提供Java那樣的靜態(tài)方法或靜態(tài)字段;
可以采用object關(guān)鍵字實(shí)現(xiàn)單例對(duì)象,具備和Java靜態(tài)方法同樣的功能;
使用object語法結(jié)構(gòu)【object是Scala中的一個(gè)關(guān)鍵字】達(dá)到靜態(tài)方法和靜態(tài)字段的目的;對(duì)象本質(zhì)上可以擁有類的所有特性,除了不能提供構(gòu)造器參數(shù);
對(duì)于任何在Java中用單例對(duì)象的地方,在Scala中都可以用object實(shí)現(xiàn):
- 作為存放工具函數(shù)或常量的地方
- 高效地共享單個(gè)不可變實(shí)例
package hhb.cn.part04
/**
* @description:
* @author: huanghongbo
**/
object Object {
println("這是一個(gè)單例對(duì)象?。。?)
def printInfo: Unit = {
println("Hello Scala Object")
}
}
object ObjectDemo {
def main(args: Array[String]): Unit = {
// 只會(huì)輸出一次這是單例對(duì)象
// val object1 = Object
// val object2 = Object
// 同樣只會(huì)輸出一次這是單例對(duì)象
Object.printInfo
Object.printInfo
}
}
Scala中的單例對(duì)象具有如下特點(diǎn):
- 創(chuàng)建單例對(duì)象不需要使用new關(guān)鍵字
- object中只有無參構(gòu)造器
- 主構(gòu)造代碼塊只能執(zhí)行一次,因?yàn)樗菃卫?/li>
伴生類與伴生對(duì)象
當(dāng)單例對(duì)象與某個(gè)類具有相同的名稱時(shí),它被稱為這個(gè)類的“伴生對(duì)象”;
類和它的伴生對(duì)象必須存在于同一個(gè)文件中,而且可以相互訪問私有成員(字段和方法);
package hhb.cn.part04
//伴生類和伴生對(duì)象在一個(gè)文件中,文件名相同
/**
* 伴生類
*/
class ClassObject {
private var name = "hhb"
def printInfo: Unit = {
//在伴生類中可以訪問伴生對(duì)象的私有成員
println(ClassObject.num)
println("Hello Object!")
}
}
/**
* 伴生對(duì)象
*/
object ClassObject {
private var num = 10
def main(args: Array[String]): Unit = {
val classObject = new ClassObject
//在伴生對(duì)象中可以訪問伴生類的私有成員
println(classObject.name)
classObject.printInfo
}
}
應(yīng)用程序與對(duì)象
每個(gè)Scala應(yīng)用程序都必須從一個(gè)對(duì)象的main方法開始,這個(gè)方法的類型為 Array[String] => Unit;
備注:main方法寫在class中是沒有意義的,在IDEA中這樣的 class 連run的圖標(biāo)都不能顯示
除了main方法以外,也可以擴(kuò)展App特質(zhì)(trait)
package hhb.cn.part04
/**
* App源碼里面有一個(gè)main方法。我們不需要寫了,只需要直接繼承App即可
*/
object AppDemo extends App {
println("Hello Spark")
}
apply方法
object 中有一個(gè)非常重要的特殊方法 -- apply方法;
apply方法通常定義在伴生對(duì)象中,目的是通過伴生類的構(gòu)造函數(shù)功能,來實(shí)現(xiàn)伴生對(duì)象的構(gòu)造函數(shù)功能;
通常我們會(huì)在類的伴生對(duì)象中定義apply方法,當(dāng)遇到類名(參數(shù)1,...參數(shù)n)時(shí)apply方法會(huì)被調(diào)用;
在創(chuàng)建伴生對(duì)象或伴生類的對(duì)象時(shí),通常不會(huì)使用new class/class() 的方式,而是直接使用 class()隱式的調(diào)用伴生對(duì)象的 apply 方法,這樣會(huì)讓對(duì)象創(chuàng)建的更加簡潔;
package hhb.cn.part04
class Student(name: String, age: Int) {
private var gender: String = _
def sayHi: Unit = {
println(s"大家好,是$name , 年齡 $age, $gender 生")
}
}
object Student extends App {
def apply(name: String, age: Int): Student = new Student(name, age)
//直接使用class類名(參數(shù)……) 這種方式隱式調(diào)用伴生對(duì)象中apply方法創(chuàng)建class Student 對(duì)象
val student = Student("zhangs", 20)
//伴生類和伴生對(duì)象可以相會(huì)訪問私有成員
student.gender = "男"
student.sayHi
}
問題:在Scala中實(shí)現(xiàn)工廠方法,讓子類聲明哪種對(duì)象應(yīng)該被創(chuàng)建,保持對(duì)象創(chuàng)建在同一位置。例如,假設(shè)要?jiǎng)?chuàng)建Animal工廠,讓其返回Cat和Dog類的實(shí)例,基于這個(gè)需求,通過實(shí)現(xiàn)Animal伴生對(duì)象的apply方法,工廠的使用者可以像這樣創(chuàng)建新的Cat和Dog實(shí)例。
package hhb.cn.part04
abstract class Animal {
def speak
}
class Dog extends Animal {
override def speak: Unit = {
println("============")
}
}
class Cat extends Animal {
override def speak: Unit = {
println("+++++++++++")
}
}
object Animal {
def apply(message: String): Animal = {
if (message == "dog") {
new Dog
} else {
new Cat
}
}
def main(args: Array[String]): Unit = {
val cat = Animal("cat")
cat.speak
val dog = Animal("dot")
dog.speak
}
}
