類和對象
scala是支持面向?qū)ο蟮拇锕浚灿蓄惡蛯ο蟮母拍罱虽獭N覀円廊豢梢曰趕cala語言來開發(fā)面向?qū)ο蟮膽?yīng)用程序主经。
創(chuàng)建類和對象
語法
- 使用
class
來定義一個類 - 使用
new
來創(chuàng)建對象
示例說明
創(chuàng)建一個Person類,并創(chuàng)建它的對象
步驟
- 創(chuàng)建一個scala項目成畦,并創(chuàng)建一個Object
- 添加main方法
- 創(chuàng)建類和對象
實現(xiàn)
- 在IDEA中創(chuàng)建項目距芬,并創(chuàng)建一個Object(main方法必須放在Object中)
- 添加main方法
- 創(chuàng)建一個Person類
- 在main方法中創(chuàng)建Person類對象
object _01ClassDemo {
// 創(chuàng)建類
class Person{}
def main(args: Array[String]): Unit = {
// 創(chuàng)建對象
val p = new Person()
println(p)
}
}
簡寫方法
- 如果類是空的,沒有任何成員循帐,可以省略
{}
- 如果構(gòu)造器的參數(shù)為空框仔,可以省略
()
object _02ClassDemo {
// 創(chuàng)建類,省略花括號
class Person
def main(args: Array[String]): Unit = {
// 創(chuàng)建對象拄养,省略括號
val person = new Person
}
}
定義和訪問成員變量
一個類會有自己的屬性离斩,例如:人這樣一個類,有自己的姓名和年齡。我們接下來學(xué)習(xí)在類中定義跛梗、和訪問成員變量寻馏。
語法:
- 在類中使用
var/val
來定義成員變量 - 對象直接使用成員變量名稱來訪問成員變量
示例說明
定義一個Person類,包含一個姓名和年齡字段
創(chuàng)建一個名為"張三"核偿、年齡為20歲的對象
打印對象的名字和年齡
步驟
- 創(chuàng)建一個Object诚欠,添加main方法
- 創(chuàng)建Person類,添加姓名字段和年齡字段漾岳,并對字段進(jìn)行初始化聂薪,讓scala自動進(jìn)行類型推斷
- 在main方法中創(chuàng)建Person類對象,設(shè)置成員變量為"張三"蝗羊、20
- 打印對象的名字和年齡
object _03ClassDemo {
class Person {
// 定義成員變量
var name = ""
var age = 0
}
def main(args: Array[String]): Unit = {
// 創(chuàng)建Person對象
val person = new Person
person.name = "zhangsan"
person.age = 20
// 獲取變量值
println(person.name)
println(person.age)
}
}
使用下劃線初始化成員變量
語法
- 在定義
var
類型的成員變量時,可以使用_
來初始化成員變量- String => null
- Int => 0
- Boolean => false
- Double => 0.0
- ...
-
val
類型的成員變量仁锯,必須要自己手動初始化
示例說明
- 定義一個Person類耀找,包含一個姓名和年齡字段
- 創(chuàng)建一個名為"張三"、年齡為20歲的對象
- 打印對象的名字和年齡
步驟
- 創(chuàng)建一個Object业崖,添加main方法
- 創(chuàng)建Person類野芒,添加姓名字段和年齡字段,指定數(shù)據(jù)類型双炕,使用下劃線初始化
- 在main方法中創(chuàng)建Person類對象狞悲,設(shè)置成員變量為"張三"、20
- 打印對象的名字和年齡
object _04ClassDemo {
class Person{
// 使用下劃線進(jìn)行初始化
var name:String = _
var age:Int = _
}
def main(args: Array[String]): Unit = {
val person = new Person
println(person.name)
println(person.age)
}
}
定義成員方法
語法
- 在scala的類中妇斤,也是使用
def
來定義成員方法
示例說明
- 創(chuàng)建一個Customer類
- 創(chuàng)建一個該類的對象摇锋,并調(diào)用printHello方法
步驟
- 創(chuàng)建一個Object,添加main方法
- 創(chuàng)建Customer類站超,添加成員變量荸恕、成員方法
- 在main方法中創(chuàng)建Customer類對象,設(shè)置成員變量值(張三死相、男)
- 調(diào)用成員方法
object _05ClassDemo {
class Customer {
var name:String = _
var sex:String = _
// 定義成員方法
def sayHi(msg:String) = {
println(msg)
}
}
def main(args: Array[String]): Unit = {
val customer = new Customer
customer.name = "張三"
customer.sex = "男"
customer.sayHi("你好")
}
}
訪問修飾符
和Java一樣融求,scala也可以通過訪問修飾符,來控制成員變量和成員方法是否可以被訪問算撮。
語法
Java中的訪問控制生宛,同樣適用于scala,可以在成員前面添加private/protected關(guān)鍵字來控制成員的可見性肮柜。但在scala中陷舅,沒有public關(guān)鍵字
,任何沒有被標(biāo)為private或protected的成員都是公共的
示例說明*
- 定義一個Person類
- 在main方法中創(chuàng)建該類的對象审洞,測試是否能夠訪問到私有成員
object _02AccessDemo {
class Person {
// 定義私有成員變量
private var name:String = _
private var age:Int = _
def getName() = name
def setName(name:String) = this.name = name
def getAge() = age
def setAge(age:Int) = this.age = age
// 定義私有成員方法
private def getNameAndAge = {
name -> age
}
}
def main(args: Array[String]): Unit = {
val person = new Person
person.setName("張三")
person.setAge(10)
println(person.getName())
println(person.getAge())
}
}
類的構(gòu)造器
當(dāng)創(chuàng)建類對象的時候蔑赘,會自動調(diào)用類的構(gòu)造器。之前使用的都是默認(rèn)構(gòu)造器,我們接下來要學(xué)習(xí)如何自定義構(gòu)造器缩赛。
語法
class 類名(var/val 參數(shù)名:類型 = 默認(rèn)值, var/val 參數(shù)名:類型 = 默認(rèn)值){
// 構(gòu)造代碼塊
}
示例說明
- 定義一個Person類耙箍,通過主構(gòu)造器參數(shù)列表定義姓名和年齡字段,并且設(shè)置它們的默認(rèn)值
- 在主構(gòu)造器中輸出"調(diào)用主構(gòu)造器"
- 創(chuàng)建"張三"對象(姓名為張三酥馍,年齡為20)辩昆,打印對象的姓名和年齡
- 創(chuàng)建"空"對象,不給構(gòu)造器傳入任何的參數(shù)旨袒,打印對象的姓名和年齡
- 創(chuàng)建"man40"對象汁针,不傳入姓名參數(shù),指定年齡為40砚尽,打印對象的姓名和年齡
object _06ConstructorDemo {
// 定義類的主構(gòu)造器
// 指定默認(rèn)值
class Person(var name:String = "", var age:Int = 0) {
println("調(diào)用主構(gòu)造器")
}
def main(args: Array[String]): Unit = {
// 給構(gòu)造器傳入?yún)?shù)
val zhangsan = new Person("張三", 20)
println(zhangsan.name)
println(zhangsan.age)
println("---")
// 不傳入任何參數(shù)
val empty = new Person
println(empty.name)
println(empty.age)
println("---")
// 指定字段進(jìn)行初始化
val man40 = new Person(age = 40)
println(man40.name)
println(man40.age)
}
}
輔助構(gòu)造器
在scala中施无,除了定義主構(gòu)造器外,還可以根據(jù)需要來定義輔助構(gòu)造器必孤。例如:允許通過多種方式猾骡,來創(chuàng)建對象,這時候就可以定義其他更多的構(gòu)造器敷搪。我們把除了主構(gòu)造器之外的構(gòu)造器稱為輔助構(gòu)造器兴想。
- 定義輔助構(gòu)造器與定義方法一樣,也使用
def
關(guān)鍵字來定義 - 這個方法的名字為
this
def this(參數(shù)名:類型, 參數(shù)名:類型) {
// 第一行需要調(diào)用主構(gòu)造器或者其他構(gòu)造器
// 構(gòu)造器代碼
}
示例說明
- 定義一個Customer類赡勘,包含一個姓名和地址字段
- 定義Customer類的主構(gòu)造器(初始化姓名和地址)
- 定義Customer類的輔助構(gòu)造器嫂便,該輔助構(gòu)造器接收一個數(shù)組參數(shù),使用數(shù)組參數(shù)來初始化成員變量
- 使用Person類的輔助構(gòu)造器來創(chuàng)建一個"zhangsan"對象
- 姓名為張三
- 地址為北京
- 打印對象的姓名闸与、地址
object _07ConstructorDemo {
class Customer(var name:String = "", var address:String = "") {
// 定義輔助構(gòu)造器
def this(arr:Array[String]) = {
// 輔助構(gòu)造器必須要調(diào)用主構(gòu)造器或者其他輔助構(gòu)造器
this(arr(0), arr(1))
}
}
def main(args: Array[String]): Unit = {
val zhangsan = new Customer(Array("張三", "北京"))
println(zhangsan.name)
println(zhangsan.address)
}
}
輔助構(gòu)造器相當(dāng)于擴(kuò)展主構(gòu)造器的屬性毙替,比如說我們還需要將customer中添加一個sex屬性,那么我們則可以在Customer中再去寫一個擴(kuò)展類。
def this(var name:String="",var address:String="",var sex:String=""){
def this(var name:String,var address:String,var sex:String){
this.sex = sex
}
}
單例對象
scala中沒有Java中的靜態(tài)成員,我們想要定義類似于Java的static變量才菠、static方法裆赵,就要使用到scala中的單例對象——object.
單例對象表示全局僅有一個對象(類似于Java static概念)
- 定義單例對象和定義類很像,就是把
class
換成object
- 在object中定義的成員變量類似于Java的靜態(tài)變量
- 可以使用object直接引用成員變量
示例說明
- 定義一個Dog單例對象,保存狗有幾條腿
- 在main方法中打印狗腿的數(shù)量
object _08ObjectDemo {
// 定義一個單例對象
object Dog {
// 定義腿的數(shù)量
val LEG_NUM = 4
}
def main(args: Array[String]): Unit = {
println(Dog.LEG_NUM)
}
}
在單例對象中定義成員方法
示例說明
- 設(shè)計一個單例對象,定義一個能夠打印分割線(15個減號)的方法
- 在main方法調(diào)用該方法,打印分割線
object _09ObjectDemo {
object PrintUtil {
// 打印分割線
def printSpliter() = {
// 字符串乘法坑填,表示返回多少個字符串
println("-" * 10)
}
}
def main(args: Array[String]): Unit = {
PrintUtil.printSpliter()
}
}
工具類案例
- 編寫一個DateUtil工具類專門用來格式化日期時間
- 定義一個方法,用于將日期(Date)轉(zhuǎn)換為年月日字符串弛姜,例如:2030-10-05
object _10ObjectDemo {
object DateUtils {
// 在object中定義的成員變量脐瑰,相當(dāng)于Java中定義一個靜態(tài)變量
// 定義一個SimpleDateFormat日期時間格式化對象
val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
// 相當(dāng)于Java中定義一個靜態(tài)方法
def format(date: Date) = simpleDateFormat.format(date)
}
// main是一個靜態(tài)方法,所以必須要寫在object中
def main(args: Array[String]): Unit = {
println(DateUtils.format(new Date()))
}
}
伴生對象
在Java中廷臼,經(jīng)常會有一些類苍在,同時有實例成員又有靜態(tài)成員绝页。例如:
public class CustomerService {
private static String SERVICE_NAME = "CustomerService";
public void save() {
// 保存客戶
System.out.println(SERVICE_NAME + ":保存客戶");
}
public static void main(String[] args) {
new CustomerService().save();
}
}
一個class和object具有同樣的名字。這個object稱為伴生對象寂恬,這個class稱為伴生類
- 伴生對象必須要和伴生類一樣的名字
- 伴生對象和伴生類在同一個scala源文件中
- 伴生對象和伴生類可以互相訪問private屬性
示例說明
-
編寫一個CustomerService類续誉,有一個save方法,打印
服務(wù)類名稱:保存客戶
編寫一個CustomerService伴生對象初肉,定義一個私有變量酷鸦,用于保存服務(wù)類名稱
創(chuàng)建CustomerService對象,調(diào)用save方法
object _11ObjectDemo {
class CustomerService {
def save() = {
println(s"${CustomerService.SERVICE_NAME}:保存客戶")
}
}
// CustomerService的伴生對象
object CustomerService {
private val SERVICE_NAME = "CustomerService"
}
def main(args: Array[String]): Unit = {
val customerService = new CustomerService()
customerService.save()
}
}
伴生對象 | apply方法
我們之前使用過這種方式來創(chuàng)建一個Array對象牙咏。
// 創(chuàng)建一個Array對象
val a = Array(1,2,3,4)
這種寫法非常簡便臼隔,不需要再寫一個new,然后敲一個空格妄壶,再寫類名摔握。我們可以通過伴生對象的apply方法來實現(xiàn)。
定義apply方法
object 伴生對象名 {
def apply(參數(shù)名:參數(shù)類型, 參數(shù)名:參數(shù)類型...) = new 類(...)
}
示例
- 定義一個Person類丁寄,它包含兩個字段:姓名和年齡
- 重寫apply方法氨淌,使用Person類名就可以創(chuàng)建對象
- 在main方法中創(chuàng)建該類的對象,并打印姓名和年齡
object _12ApplyDemo {
class Person(var name:String = "", var age:Int = 0)
object Person {
// 定義apply方法狡逢,接收兩個參數(shù)
def apply(name:String, age:Int) = new Person(name, age)
}
def main(args: Array[String]): Unit = {
// 使用伴生對象名稱來創(chuàng)建對象
val zhangsan = Person("張三", 20)
println(zhangsan.name)
println(zhangsan.age)
}
}
繼承
scala語言是支持面向?qū)ο缶幊痰模覀円部梢允褂胹cala來實現(xiàn)繼承拼卵,通過繼承來減少重復(fù)代碼奢浑。
定義語法
- scala和Java一樣,使用extends關(guān)鍵字來實現(xiàn)繼承
- 可以在子類中定義父類中沒有的字段和方法腋腮,或者重寫父類的方法
- 類和單例對象都可以從某個父類繼承
class/object 子類 extends 父類 {
..
}
示例說明
- 定義一個Person類雀彼,再定義一個Student類,繼承自Person類
- 創(chuàng)建一個Student類對象實例即寡,并設(shè)置name為“張三”
- 打印姓名
class Person {
var name = "super"
def getName = this.name
}
class Student extends Person
object Main13 {
def main(args: Array[String]): Unit = {
val p1 = new Person()
val p2 = new Student()
p2.name = "張三"
println(p2.getName)
}
}
override和super
類似于Java語言徊哑,我們在子類中使用override需要來重寫父類的成員,可以使用super來引用父類
- 子類要覆蓋父類中的一個方法聪富,必須要使用override關(guān)鍵字
- 使用override來重寫一個val字段
- 使用super關(guān)鍵字來訪問父類的成員方法
-
示例說明
- 定義一個Person類莺丑,包含
- 姓名字段(不可重新賦值)
- 獲取姓名方法
- 定義一個Student類
- 重寫姓名字段
- 重寫獲取姓名方法,返回"hello, " + 姓名
- 創(chuàng)建Student對象示例墩蔓,調(diào)用它的getName方法
- 定義一個Person類莺丑,包含
class Person {
val name = "super"
def getName = name
}
class Student extends Person {
// 重寫val字段
override val name: String = "child"
// 重寫getName方法
override def getName: String = "hello, " + super.getName
}
object Main13 {
def main(args: Array[String]): Unit = {
println(new Student().getName)
}
}
類型判斷
scala中對象提供isInstanceOf和asInstanceOf方法梢莽。
- isInstanceOf判斷對象是否為指定類的對象
- asInstanceOf將對象轉(zhuǎn)換為指定類型
// 判斷對象是否為指定類型
val trueOrFalse:Boolean = 對象.isInstanceOf[類型]
// 將對象轉(zhuǎn)換為指定類型
val 變量 = 對象.asInstanceOf[類型]
示例說明
- 定義一個Person類
- 定義一個Student類繼承自Person類
- 創(chuàng)建一個Student類對象
- 判斷該對象是否為Student類型,如果是奸披,將其轉(zhuǎn)換為Student類型并打印該對象
class Person3
class Student3 extends Person3
object Main3 {
def main(args: Array[String]): Unit = {
val s1:Person3 = new Student3
// 判斷s1是否為Student3類型
if(s1.isInstanceOf[Student3]) {
// 將s1轉(zhuǎn)換為Student3類型
val s2 = s1.asInstanceOf[Student3]
println(s2)
}
}
}
getClass和classOf
isInstanceOf 只能判斷對象是否為指定類以及其子類的對象昏名,而不能精確的判斷出,對象就是指定類的對象阵面。如果要求精確地判斷出對象就是指定類的對象轻局,那么就只能使用 getClass 和 classOf 洪鸭。
- p.getClass可以精確獲取對象的類型
- classOf[x]可以精確獲取類型
- 使用==操作符可以直接比較類型
示例說明
- 定義一個Person類
- 定義一個Student類繼承自Person類
- 創(chuàng)建一個Student類對象,并指定它的類型為Person類型
- 測試使用isInstance判斷該對象是否為Person類型
- 測試使用getClass/classOf判斷該對象是否為Person類型
- 測試使用getClass/classOf判斷該對象是否為Student類型
class Person4
class Student4 extends Person4
object Student4{
def main(args: Array[String]) {
val p:Person4=new Student4
//判斷p是否為Person4類的實例
println(p.isInstanceOf[Person4])//true
//判斷p的類型是否為Person4類
println(p.getClass == classOf[Person4])//false
//判斷p的類型是否為Student4類
println(p.getClass == classOf[Student4])//true
}
}
抽象類
如果類的某個成員在當(dāng)前類中的定義是不包含完整的仑扑,它就是一個抽象類
不完整定義有兩種情況:
- 方法沒有方法體(抽象方法)
- 變量沒有初始化(抽象字段)
示例
- 設(shè)計4個類览爵,表示上述圖中的繼承關(guān)系
- 每一個形狀都有自己求面積的方法,但是不同的形狀計算面積的方法不同
步驟
- 創(chuàng)建一個Shape抽象類夫壁,添加一個area抽象方法拾枣,用于計算面積
- 創(chuàng)建一個Square正方形類,繼承自Shape盒让,它有一個邊長的主構(gòu)造器梅肤,并實現(xiàn)計算面積方法
- 創(chuàng)建一個長方形類,繼承自Shape邑茄,它有一個長姨蝴、寬的主構(gòu)造器,實現(xiàn)計算面積方法
- 創(chuàng)建一個圓形類肺缕,繼承自Shape左医,它有一個半徑的主構(gòu)造器,并實現(xiàn)計算面積方法
- 編寫main方法同木,分別創(chuàng)建正方形浮梢、長方形、圓形對象彤路,并打印它們的面積
// 創(chuàng)建形狀抽象類
abstract class Shape {
def area:Double
}
// 創(chuàng)建正方形類
class Square(var edge:Double /*邊長*/) extends Shape {
// 實現(xiàn)父類計算面積的方法
override def area: Double = edge * edge
}
// 創(chuàng)建長方形類
class Rectangle(var length:Double /*長*/, var width:Double /*寬*/) extends Shape {
override def area: Double = length * width
}
// 創(chuàng)建圓形類
class Cirle(var radius:Double /*半徑*/) extends Shape {
override def area: Double = Math.PI * radius * radius
}
object Main6 {
def main(args: Array[String]): Unit = {
val s1:Shape = new Square(2)
val s2:Shape = new Rectangle(2,3)
val s3:Shape = new Cirle(2)
println(s1.area)
println(s2.area)
println(s3.area)
}
}
抽象字段
在scala中秕硝,也可以定義抽象的字段。如果一個成員變量是沒有初始化洲尊,我們就認(rèn)為它是抽象的远豺。
示例說明
- 創(chuàng)建一個Person抽象類,它有一個String抽象字段WHO_AM_I
- 創(chuàng)建一個Student類坞嘀,繼承自Person類躯护,重寫WHO_AM_I字段,初始化為學(xué)生
- 創(chuàng)建一個Policeman類丽涩,繼承自Person類棺滞,重寫WHO_AM_I字段,初始化警察
- 添加main方法矢渊,分別創(chuàng)建Student/Policeman的實例检眯,然后分別打印WHO_AM_I
// 定義一個人的抽象類
abstract class Person6 {
// 沒有初始化的val字段就是抽象字段
val WHO_AM_I:String
}
class Student6 extends Person6 {
override val WHO_AM_I: String = "學(xué)生"
}
class Policeman6 extends Person6 {
override val WHO_AM_I: String = "警察"
}
object Main6 {
def main(args: Array[String]): Unit = {
val p1 = new Student6
val p2 = new Policeman6
println(p1.WHO_AM_I)
println(p2.WHO_AM_I)
}
}
匿名內(nèi)部類
匿名內(nèi)部類是沒有名稱的子類,直接用來創(chuàng)建實例對象昆淡。Spark的源代碼中有大量使用到匿名內(nèi)部類锰瘸。
示例說明
- 創(chuàng)建一個Person抽象類,并添加一個sayHello抽象方法
- 添加main方法昂灵,通過創(chuàng)建匿名內(nèi)部類的方式來實現(xiàn)Person
- 調(diào)用匿名內(nèi)部類對象的sayHello方法
abstract class Person7 {
def sayHello:Unit
}
object Main7 {
def main(args: Array[String]): Unit = {
// 直接用new來創(chuàng)建一個匿名內(nèi)部類對象
val p1 = new Person7 {
override def sayHello: Unit = println("我是一個匿名內(nèi)部類")
}
p1.sayHello
}
}
特質(zhì)(trait)
scala中沒有Java中的接口(interface)避凝,替代的概念是——特質(zhì)
- 特質(zhì)是scala中代碼復(fù)用的基礎(chǔ)單元
- 它可以將方法和字段定義封裝起來舞萄,然后添加到類中
- 與類繼承不一樣的是,類繼承要求每個類都只能繼承
一個
超類管削,而一個類可以添加任意數(shù)量
的特質(zhì)倒脓。 - 特質(zhì)的定義和抽象類的定義很像,但它是使用
trait
關(guān)鍵字
定義特質(zhì)
trait 名稱 {
// 抽象字段
// 抽象方法
}
繼承特質(zhì)
class 類 extends 特質(zhì)1 with 特質(zhì)2 {
// 字段實現(xiàn)
// 方法實現(xiàn)
}
使用
extends
來繼承trait(scala不論是類還是特質(zhì)含思,都是使用extends關(guān)鍵字)如果要繼承多個trait崎弃,則使用
with
關(guān)鍵字
示例 | 繼承單個trait
示例說明
- 創(chuàng)建一個Logger特質(zhì),添加一個接受一個String類型參數(shù)的log抽象方法
- 創(chuàng)建一個ConsoleLogger類含潘,繼承Logger特質(zhì)饲做,實現(xiàn)log方法,打印消息
- 添加main方法遏弱,創(chuàng)建ConsoleLogger對象盆均,調(diào)用log方法
trait Logger {
// 抽象方法
def log(message:String)
}
class ConsoleLogger extends Logger {
override def log(message: String): Unit = println("控制臺日志:" + message)
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("這是一條日志")
}
示例 | 繼承多個trait
示例說明
- 創(chuàng)建一個MessageSender特質(zhì),添加send方法
- 創(chuàng)建一個MessageReceiver特質(zhì)漱逸,添加receive方法
- 創(chuàng)建一個MessageWorker實現(xiàn)這兩個特質(zhì)
- 在main中調(diào)用泪姨,分別調(diào)用send方法、receive方法
trait MessageSender {
def send(msg:String)
}
trait MessageReceive {
def receive():String
}
class MessageWorker extends MessageSender with MessageReceive {
override def send(msg: String): Unit = println(s"發(fā)送消息:${msg}")
override def receive(): String = "你好饰抒!我叫一個好人肮砾!"
}
def main(args: Array[String]): Unit = {
val worker = new MessageWorker
worker.send("hello")
println(worker.receive())
}
示例 | object繼承trait
示例說明
- 創(chuàng)建一個Logger特質(zhì),添加一個log抽象方法
- 創(chuàng)建一個ConsoleLogger的object袋坑,實現(xiàn)LoggerForObject特質(zhì)仗处,實現(xiàn)log方法,打印消息
- 編寫main方法咒彤,調(diào)用ConsoleLogger的log方法
trait Logger {
def log(message:String)
}
object ConsoleLogger extends Logger {
override def log(message: String): Unit = println("控制臺消息:" + message)
}
def main(args: Array[String]): Unit = {
ConsoleLogger.log("程序退出!")
}
特質(zhì) | 定義具體的方法
示例說明
定義一個Logger特質(zhì)疆柔,添加log實現(xiàn)方法
定義一個UserService類咒精,實現(xiàn)Logger特質(zhì)
添加add方法镶柱,打印"添加用戶"
添加main方法
創(chuàng)建UserService對象實例
調(diào)用add方法
trait LoggerDetail {
// 在trait中定義具體方法
def log(msg:String) = println(msg)
}
class UserService extends LoggerDetail {
def add() = log("添加用戶")
}
object MethodInTrait {
def main(args: Array[String]): Unit = {
val userService = new UserService
userService.add()
}
}
- 在trait中可以定義具體字段和抽象字段
- 繼承trait的子類自動擁有trait中定義的字段
- 字段直接被添加到子類中
步驟
- 創(chuàng)建Logger特質(zhì)
- 定義一個SimpleDateFormat字段,用來格式化日期(顯示到時間)
- 定義一個TYPE抽象字段模叙,用于定義輸出的信息
- 創(chuàng)建一個log抽象方法歇拆,用于輸出日志
- 創(chuàng)建ConsoleLogger類,實現(xiàn)TYPE抽象字段和log方法
- 添加main方法
- 創(chuàng)建ConsoleLogger類對象
- 調(diào)用log方法
trait Logger {
val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")
def log(msg:String)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = {
val info = s"${sdf.format(new Date())}:控制臺消息:${msg}"
println(info)
}
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger()
logger.log("NullPointerException")
}
實現(xiàn)模板模式
示例說明
- 編寫一個日志輸出工具范咨,分別有info故觅、warn、error三個級別的日志輸出
- 日志輸出的方式要求設(shè)計為可擴(kuò)展的渠啊,例如:可以輸出到控制臺输吏、將來也可以擴(kuò)展輸出到文件、數(shù)據(jù)庫等
實現(xiàn)步驟
- 添加一個Logger特質(zhì)
- 添加一個log抽象方法
- 添加一個info替蛉、warn贯溅、error具體方法拄氯,這幾個方法調(diào)用log抽象方法
- 創(chuàng)建ConsoleLogger類,實現(xiàn)Logger特質(zhì)
- 添加main方法
- 創(chuàng)建ConsoleLogger類對象
- 分別調(diào)用info它浅、warn译柏、error方法輸出日志
trait Logger {
def log(msg:String)
def info(msg:String) = log("INFO:" + msg)
def warn(msg:String) = log("WARN:" + msg)
def error(msg:String) = log("ERROR:" + msg)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = {
println(msg)
}
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.info("信息日志")
logger.warn("警告日志")
logger.error("錯誤日志")
}
對象混入trait
示例說明
- 給一個對象添加一些額外的行為
步驟
- 創(chuàng)建一個Logger特質(zhì)
- 添加一個log實現(xiàn)方法,打印參數(shù)
- 創(chuàng)建一個UserService類
- 添加main方法
- 創(chuàng)建UserService對象姐霍,混入Logger特質(zhì)
- 調(diào)用log方法
trait Logger {
def log(msg:String) = println(msg)
}
class UserService
def main(args: Array[String]): Unit = {
val service = new UserService with Logger
service.log("混入的方法")
}
調(diào)用鏈
步驟
- 定義一個HandlerTrait特質(zhì)
- 定義一個具體的handler方法鄙麦,打印"處理數(shù)據(jù)..."
- 定義一個DataValidHandlerTrait,繼承HandlerTrait特質(zhì)
- 重寫handler方法镊折,打印"驗證數(shù)據(jù)"
- 調(diào)用父特質(zhì)的handler方法
- 定義一個SignatureValidHandlerTrait胯府,繼承HandlerTrait特質(zhì)
- 重寫Handler方法
- 打印"檢查簽名"
- 調(diào)用父特質(zhì)的handler方法
- 創(chuàng)建一個PaymentService類
- 繼承DataValidHandlerTrait
- 繼承SignatureValidHandlerTrait
- 定義pay方法
- 打印"準(zhǔn)備支付"
- 調(diào)用父特質(zhì)的handler方法
- 添加main方法
- 創(chuàng)建PaymentService對象實例
- 調(diào)用pay方法
trait HandlerTrait {
def handle(data:String) = println("處理數(shù)據(jù)...")
}
trait DataValidHanlderTrait extends HandlerTrait {
override def handle(data:String): Unit = {
println("驗證數(shù)據(jù)...")
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("校驗簽名...")
super.handle(data)
}
}
class PayService extends DataValidHanlderTrait with SignatureValidHandlerTrait {
override def handle(data: String): Unit = {
println("準(zhǔn)備支付...")
super.handle(data)
}
}
def main(args: Array[String]): Unit = {
val service = new PayService
service.handle("支付參數(shù)")
}
// 程序運行輸出如下:
// 準(zhǔn)備支付...
// 檢查簽名...
// 驗證數(shù)據(jù)...
// 處理數(shù)據(jù)...
class PayService extends DataValidHanlderTrait with SignatureValidHandlerTrait
with后面的會先執(zhí)行
trait的構(gòu)造機(jī)制
如果一個類實現(xiàn)了多個trait,那這些trait是如何構(gòu)造的呢腌乡?
- trait也有構(gòu)造代碼盟劫,但和類不一樣,特質(zhì)不能有構(gòu)造器參數(shù)
- 每個特質(zhì)只有
一個無參數(shù)
的構(gòu)造器与纽。 - 一個類繼承另一個類侣签、以及多個trait,當(dāng)創(chuàng)建該類的實例時急迂,它的構(gòu)造順序如下:
- 執(zhí)行父類的構(gòu)造器
-
從左到右
依次執(zhí)行trait的構(gòu)造器 - 如果trait有父trait影所,先構(gòu)造父trait,如果多個trait有同樣的父trait僚碎,則只初始化一次
- 執(zhí)行子類構(gòu)造器
示例說明
- 定義多個特質(zhì)猴娩,然后用一個類去實現(xiàn)它們
- 測試trait的構(gòu)造順序
步驟
- 創(chuàng)建一個Logger特質(zhì),在構(gòu)造器中打印"執(zhí)行Logger構(gòu)造器!"
- 創(chuàng)建一個MyLogger特質(zhì)勺阐,繼承自Logger特質(zhì)卷中,,在構(gòu)造器中打印"執(zhí)行MyLogger構(gòu)造器!"
- 創(chuàng)建一個TimeLogger特質(zhì)渊抽,繼承自Logger特質(zhì)蟆豫,在構(gòu)造器中打印"執(zhí)行TimeLogger構(gòu)造器!"
- 創(chuàng)建一個Person類,在構(gòu)造器中打印"執(zhí)行Person構(gòu)造器!"
- 創(chuàng)建一個Student類懒闷,繼承自Person十减、MyLogger、TimeLogge特質(zhì)愤估,在構(gòu)造器中打印"執(zhí)行Student構(gòu)造器!"
- 添加main方法帮辟,實例化Student_One類,觀察輸出玩焰。
trait Logger {
println("執(zhí)行Logger構(gòu)造器")
}
trait MyLogger extends Logger {
println("執(zhí)行MyLogger構(gòu)造器")
}
trait TimeLogger extends Logger {
println("執(zhí)行TimeLogger構(gòu)造器")
}
class Person{
println("執(zhí)行Person構(gòu)造器")
}
class Student extends Person with TimeLogger with MyLogger {
println("執(zhí)行Student構(gòu)造器")
}
def main(args: Array[String]): Unit = {
new Student
}
// 程序運行輸出如下:
// 執(zhí)行Person構(gòu)造器
// 執(zhí)行Logger構(gòu)造器
// 執(zhí)行TimeLogger構(gòu)造器
// 執(zhí)行MyLogger構(gòu)造器
// 執(zhí)行Student構(gòu)造器
trait繼承class
trait也可以繼承class的由驹。特質(zhì)會將class中的成員都繼承下來。
示例說明
- 定義一個特質(zhì)昔园,繼承自一個class
步驟
- 創(chuàng)建一個MyUtils類蔓榄,定義printMsg方法
- 創(chuàng)建一個Logger特質(zhì)闹炉,繼承自MyUtils,定義log方法
- 創(chuàng)建一個Person類润樱,添加name字段
- 繼承Logger特質(zhì)
- 實現(xiàn)sayHello方法渣触,調(diào)用log方法
- 添加main方法,創(chuàng)建一個Person對象壹若,調(diào)用sayHello方法
class MyUtil {
def printMsg(msg:String) = println(msg)
}
trait Logger extends MyUtil {
def log(msg:String) = printMsg("Logger:" + msg)
}
class Person extends Logger {
def sayHello() = log("你好")
}
def main(args: Array[String]): Unit = {
val person = new Person
person.sayHello()
}