2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)
目錄
特質(trait)
作為接口使用
定義具體的方法
定義具體方法和抽象方法
定義具體的字段和抽象的字段
實例對象混入trait
trait調用鏈?
trait的構造機制
trait繼承class
特質(trait)
OLTP = online transaction processing
大數據:OLAP = online analysis processing
scala中沒有interfact的接口
可以用trait來實現接口的功能。
同時trait比接口更強大
- 特質是scala中代碼復用的基礎單元
- 它可以將方法和字段定義封裝起來,然后添加到類中
- 與類繼承不一樣的是,類繼承要求每個類都只能繼承一個超類,而一個類可以添加任意數量的特質。
- 特質的定義和抽象類的定義很像,但它是使用trait關鍵字
1. trait 中可以有 抽象的 也可以有 具體的
2. 一個類可以實現多個trait(通過with關鍵字)
3. 單個對象也可以附件多個trait
4. trait本身也可以繼承其它class
通過這3個特性,我們可以體現出來。trait是一種 代碼復用的最小單元
我們可以將想要的特性功能封裝到trait中
不管是讓class去附加 還是單個對象去附加 都OK
同時附加的數量不受到影響。
接下來,我們就來學習trait的幾種用法。
?
作為接口使用
- 使用extends來繼承trait(scala不論是類還是特質,都是使用extends關鍵字)
- 如果要繼承多個trait,則使用with關鍵字
案例1:繼承單個trait
實現步驟:
創建一個Logger特質,添加一個接受一個String類型參數的log抽象方法
創建一個ConsoleLogger類,繼承Logger特質,實現log方法,打印消息
添加main方法,創建ConsoleLogger對象,調用log方法
示例代碼:
trait?Logger {// 抽象方法def?log(msg:String)
}class?ConsoleLogger extends?Logger {override?def?log(msg:?String):?Unit?=?println(msg)
}object?LoggerTrait {def?main(args:?Array[String]):?Unit?=?{val?logger =?new?ConsoleLoggerlogger.log("控制臺日志: 這是一條Log")}
}
?
案例2:繼承多個trait
實現步驟:
在上一個案例的基礎上,創建一個MessageSender特質,添加接受一個String類型參數的send方法
再讓ConsoleLogger實現一個MessageSender特質,并實現它的send方法,打印"發送消息..."
在main中調用,send方法
示例代碼:
trait?Logger {// 抽象方法def?log(msg:String)
}trait?MessageSender {def?send(msg:String)
}class?ConsoleLogger extends?Logger with?MessageSender {override?def?log(msg:?String):?Unit?=?println(msg)override?def?send(msg:?String):?Unit?=?println(s"發送消息:${msg}")
}object?LoggerTrait {def?main(args:?Array[String]):?Unit?=?{val?logger =?new?ConsoleLoggerlogger.log("控制臺日志: 這是一條Log")logger.send("你好!")}
}
?
案例3:object繼承trait
?
實現步驟:
創建一個LoggerForObject特質,添加一個接受一個String類型參數的log抽象方法
創建一個ConsoleLogger的object,實現LoggerForObject特質,實現log方法,打印消息
編寫main方法,調用ConsoleLogger的log方法
?
trait?LoggerForObject {// 抽象方法def?log(msg:String)
}// object也可以繼承trait
object?ConsoleLogger extends?LoggerForObject {override?def?log(msg:?String):?Unit?=?println(s"控制臺信息 $msg")
}object?ObjectTrait {def?main(args:?Array[String]):?Unit?=?{ConsoleLogger.log("程序退出")}
}
?
在trait中可以定義抽象方法,不寫方法體就是抽象方法
和繼承類一樣,使用extends來繼承trait
繼承多個trait,使用with關鍵字
單例對象也可以繼承trait
?
定義具體的方法
和類一樣,trait中還可以定義具體的方法。·
案例:在trait中定義具體方法
實現步驟:
1. 定義一個LoggerDetail特質
- 添加接受一個String類型的log方法,并打印參數
2. 定義一個UserService類,實現LoggerDetail特質
-??添加add方法,調用log方法打印"添加用戶"
3. 添加main方法
- 創建UserService對象實例
- 調用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?UserServiceuserService.add()}
}
?
定義具體方法和抽象方法
?
- 在trait中,可以混合使用具體方法和抽象方法
- 使用具體方法依賴于抽象方法,而抽象方法可以放到繼承trait的子類中實現,這種設計方式也稱為模板模式
?
案例:實現一個模板模式
實現步驟:
1. 添加一個LoggerFix特質
?
- 添加一個log抽象方法,接收String參數
- 添加一個info具體方法,接收String參數,調用log方法,參數添加"INFO:"
- 添加一個warn具體方法,接收String參數,調用log方法,參數添加"WARN:"
- 添加一個error具體方法,接收String參數,調用log方法,參數添加"ERROR:"
2. 創建ConsoleLoggerFix類
?
- 實現LoggerFix特質
- 實現log方法,打印參數
3. 添加main方法
?
- 創建ConsoleLoggerFix類對象
- 調用info方法
- 調用warn方法
- 調用error方法
示例代碼:
trait?Logger08 {// 抽象方法def?log(msg:String)// 具體方法(該方法依賴于抽象方法logdef?info(msg:String)?=?log("INFO:"?+?msg)def?warn(msg:String)?=?log("WARN:"?+?msg)def?error(msg:String)?=?log("ERROR:"?+?msg)
}class?ConsoleLogger08 extends?Logger08 {override?def?log(msg:?String):?Unit?=?println(msg)
}object?Trait08 {def?main(args:?Array[String]):?Unit?=?{val?logger08 =?new?ConsoleLogger08logger08.info("這是一條普通信息")logger08.warn("這是一條警告信息")logger08.error("這是一條錯誤信息")}
}
?
定義具體的字段和抽象的字段
- 在trait中可以定義具體字段和抽象字段
- 繼承trait的子類自動擁有trait中定義的字段
- 字段直接被添加到子類中
案例:在trait中定義具體的字段和抽象的字段
實現步驟:
1. 創建LoggerEx特質
- 定義一個sdf具體字段,類型為SimpleDateFormat,用來格式化日志(顯示到時間)
- 創建一個INFO具體字段,類型為String,初始化為:"信息:當前日期"
- 創建一個TYPE抽象字段,類型為String
- 創建一個log抽象方法,參數為String類型
2. 創建ConsoleLoggerEx類
?
- 實現LoggerEx特質
- 實現TYPE抽象字段,初始化為"控制臺"
- 實現log抽象方法,分別打印TYPE字段,INFO字段和參數
3. 添加main方法
?
- 創建ConsoleLoggerEx類對象
- 調用log方法
?
示例代碼:
trait?LoggerEx {// 具體字段val?sdf =?new?SimpleDateFormat("yyyy-MM-dd HH:mm")val?INFO =?"信息:"?+?sdf.format(new?Date)// 抽象字段val?TYPE:String// 抽象方法def?log(msg:String)
}class?ConsoleLoggerEx extends?LoggerEx {// 實現抽象字段override?val?TYPE:?String =?"控制臺"// 實現抽象方法override?def?log(msg:String):?Unit?=?print(s"$TYPE$INFO?$msg")
}object?FieldInTrait {def?main(args:?Array[String]):?Unit?=?{val?logger =?new?ConsoleLoggerExlogger.log("這是一條消息")}
}
?
實例對象混入trait
- trait還可以混入到實例對象中,給對象實例添加額外的行為
- 只有混入了trait的對象才具有trait中的方法,其他的類對象不具有trait中的行為
- 使用with將trait混入到實例對象中
案例:將一個特質混入到一個對象中
實現步驟:
1. 創建一個LoggerMix混入
?
- 添加一個log方法,參數為String類型
- 打印參數
2. 創建一個UserService類
3. 添加main方法
?
- 創建UserService對象,還如LoggerMix特質
- 調用log方法
?
示例代碼:
trait?LoggerMix {def?log(msg:String)?=?println(msg)
}class?UserServiceobject?FixedInClass {def?main(args:?Array[String]):?Unit?=?{// 使用with關鍵字直接將特質混入到對象中val?userService =?new?UserService with?LoggerMixuserService.log("你好")}
}
?
trait調用鏈?
責任鏈模式
?
需求:
?
類繼承了多個trait后,可以依次調用多個trait中的同一個方法,只要讓多個trait中的同一個方法在最后都依次執行super關鍵字即可。類中調用多個tait中都有這個方法時,首先會從最右邊的trait方法開始執行,然后依次往左執行,形成一個調用鏈條。
案例:實現一個模擬支付過程的調用鏈
實現步驟:
1. 定義一個HandlerTrait特質
-?定義一個具體的handler方法,接收String參數,打印"處理數據..."
2. 定義一個DataValidHandlerTrait,繼承HandlerTrait特質
?
- 重寫handler方法
- 打印"驗證數據"
- 調用父特質的handler方法
3. 定義一個SignatureValidHandlerTrait,繼承HandlerTrait特質
?
- 重寫Handler方法
- 打印"檢查簽名"
- 調用父特質的handler方法
4. 創建一個PaymentService類
?
- 繼承DataValidHandlerTrait
- 繼承SignatureValidHandlerTrait
- 定義pay方法
- 打印"準備支付"
- 調用父特質的handler方法
5.添加main方法
?
- 創建PaymentService對象實例
- 調用pay方法
?
示例:
// 支付數據處理
trait?HandlerTrait {def?handle(data:?String)?=?{println("處理數據...")}
}// 數據校驗處理
trait?DataValidHandlerTrait extends?HandlerTrait {override?def?handle(data:?String)?=?{println("驗證數據...")super.handle(data)}
}// 簽名校驗處理
trait?SignatureValidHandlerTrait extends?HandlerTrait {override?def?handle(data:?String)?=?{println("檢查簽名...")super.handle(data)}
}// 支付服務
class?PaymentService extends?DataValidHandlerTrait with?SignatureValidHandlerTrait {def?pay(data:String)?=?{println("準備支付...")this.handle(data)}
}object?PaymentService {def?main(args:?Array[String])?{val?payService =?new?PaymentService()payService.pay("signature:10233123||md5:123889a3d5s1f6123||data:{order:001,money:200}")}
}// 程序運行輸出如下:
// 準備支付...
// 檢查簽名...
// 驗證數據...
// 處理數據...
?
trait的構造機制
?
責任鏈模式和類構造順序不同
責任鏈(指的是對父類方法的調用):
- 從右向左, 同層優先
父類構造順序:
- 從左到右,父類優先
- trait也有構造代碼,但和類不一樣,特質不能有構造器參數
- 每個特質只有一個無參數的構造器。
- 一個類繼承另一個類、以及多個trait,當創建該類的實例時,它的構造順序如下:
- 執行父類的構造器
- 從左到右依次執行trait的構造器
- 如果trait有父trait,先構造父trait,如果多個trait有同樣的父trait,則只初始化一次
- 執行子類構造器
?
案例:演示trait的構造順序
實現步驟:
- 創建一個Person_One特質,在構造器中打印"執行Person構造器!"
- 創建一個Logger_One特質,在構造器中打印"執行Logger構造器!"
- 創建一個MyLoggerOne特質,繼承自LoggerOne特質,,在構造器中打印"執行MyLogger構造器!"
- 創建一個TimeLoggerOne特質,繼承自LoggerOne特質,在構造器中打印"執行TimeLogger構造器!"
- 創建一個StudentOne類,繼承自PersonOne、MyLoggerOne、TimeLoggerOne特質,在構造器中打印"執行Student構造器!"
- 添加main方法,實例化Student_One類,觀察輸出。
?
示例代碼:
?
class?Person_One {println("執行Person構造器!")
}
trait?Logger_One {println("執行Logger構造器!")
}
trait?MyLogger_One extends?Logger_One {println("執行MyLogger構造器!")
}
trait?TimeLogger_One extends?Logger_One {println("執行TimeLogger構造器!")
}
class?Student_One extends?Person_One with?MyLogger_One with?TimeLogger_One {println("執行Student構造器!")}
object?exe_one {def?main(args:?Array[String]):?Unit?=?{val?student =?new?Student_One}
}// 程序運行輸出如下:
// 執行Person構造器!
// 執行Logger構造器!
// 執行MyLogger構造器!
// 執行TimeLogger構造器!
// 執行Student構造器!
?
trait繼承class
- trait也可以繼承class
- 這個class就會成為所有該trait子類的超級父類。
單個實例對象附加trait的時候, 無法在任何地方調用trait父類的成員
必須是class繼承trait才可以
案例:定義一個特質,繼承自一個class
?
實現步驟:
1. 創建一個MyUtils類
- 定義printMsg方法,接收String參數,并打印參數
2. 創建一個Logger_Two特質
?
- 繼承自MyUtils
- 定義log方法,接收String參數,并打印一個前綴和參數
3. 創建一個Person_Three類
?
- 實現String類型name字段的主構造器
- 繼承Logger_Two特質
- 實現sayHello方法
- 調用log,傳入"Hi, I‘m "加上name字段
- 調用printMsg,傳入"Hello, I'm "加上name字段
4. 添加main方法
?
- 創建一個Person_Three對象
- 調用sayHello方法
?
示例:
class?MyUtil {def?printMsg(msg:?String)?=?println(msg)
}
trait?Logger_Two extends?MyUtil {def?log(msg:?String)?=?this.printMsg("log: "?+?msg)
}
class?Person_Three(val?name:?String)?extends?Logger_Two {def?sayHello {this.log("Hi, I'm "?+?this.name)this.printMsg("Hello, I'm "?+?this.name)}
}
object?Person_Three{def?main(args:?Array[String])?{val?p=new?Person_Three("Tom")p.sayHello//執行結果:
// ?????log: Hi, I'm Tom
// ?????Hello, I'm Tom}
}
?
總結
以上是生活随笔為你收集整理的2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年大数据常用语言Scala(二十
- 下一篇: 2021年大数据Flink(四十四):