Scala
大資料Scala系列之特質, 特質的定義除了使用關鍵字trait之外,與類定義無異。
特質用來在類之間進行介面或者屬性的共享。類和物件都可以繼承特質,特質不能被例項化,因此也沒有引數。
一旦特質被定義了,就可以使用extends或者with在類中混入特質。
1 作為介面使用的特質
特質的定義:
trait Logger{
//
這是一個抽象方法,特質中未被實現的方法預設是抽象的,不需要
abstract
關鍵字修飾
def log(msg:String)
}
子類對特質的實現:
class ConsoleLogger
extends Logger{
//
重寫抽象方法,不需要
override
def log(msg:String){println(msg)}
}
2 帶有具體實現的特質
trait ConsoleLogger{
//
注意與
Java
中介面的不同
def log(msg:String){println(msg)}
}
特質的使用
class SavingAccount
extends Account
with ConsoleLogger{
def withdraw(amount:Double){
if(amount >balance) log("Insufficent funds")
else balance -= amount
}
}
3 帶有特質的物件
scala自帶有Logged特質,但是沒有實現
trait Logged{
def log(msg:String){}
}
如果在類定義中使用了該特質
//
該類中,其中的日誌資訊不會被記錄
class SavingAccount
extends Account
with Logged{
def withdraw(amount:Double){
if(amount >balance) log("Insufficent funds")
else balance -= amount
}
}
標準的ConsoleLogger擴充套件自Logger
class ConsoleLogger
extends Logger{
//
重寫抽象方法,不需要
override
def log(msg:String){println(msg)}
}
可以在建立物件的時候,加入該特質:
val acct1= new SavingAccount with ConsoleLogger
這樣,建立同一類物件,卻可以加入不同的特質
val acct2= new SavingAccount with FileLogger
4 多個疊加的特質
可以為類或者物件新增多個互相呼叫的特質,特質的執行順序,取決於特質被新增的順序
trait Logged{
def log(msg:String)
}
trait ConsoleLogger
extends Logged{
//
重寫抽象方法,不需要
override
def log(msg: String) ={println(msg)}
}
//
給
log
加上時間戳
trait TimestampLogger
extends ConsoleLogger {
override
def log(msg: String) {
super.log(s"${java.time.Instant.now()} $msg")
}
}
//
截斷過於冗長的日誌資訊
trait ShortLogger
extends ConsoleLogger{
val maxLength = 15
override
def log(msg: String) {
super.log(
if(msg.length <=maxLength)msg
else
s"${msg.substring(0,maxLength-3)}...")
}
}
//
定義超類
class Account {
protected
var balance:Double = 0
}
class SavingAccount
extends Account
with ConsoleLogger{
def withdraw(amount:Double){
if(amount >balance) log("Insufficent funds")
else balance = balance - amount
}
}
object test{
def main(args: Array[String]): Unit = {
val acct1 =
new SavingAccount
with ConsoleLogger
with TimestampLogger
with ShortLogger
val acct2 =
new SavingAccount
with ConsoleLogger
with ShortLogger
with TimestampLogger
acct1.withdraw(100.0)
acct2.withdraw(100.0)
}
}
//res:
//ShortLogger
的
log
方法先被執行,然後它的
super.log
呼叫的是
TimestampLogger
的
log
方法,最後呼叫
ConsoleLogger
的方法將資訊列印出來
2018-06-15T16:50:28.448Z Insufficent ...
//
先是
TimestampLogger
的
log
方法被執行,然後它的
super.log
呼叫的是
ShortLogger
的
log
方法,最後呼叫
ConsoleLogger
的方法將資訊列印出來
2018-06-15T1...
5 使用特質統一程式設計
import scala.collection.mutable.ArrayBuffer
trait Pet {
val name: String
}
class Cat(
val name: String)
extends Pet
class Dog(
val name: String)
extends Pet
val dog =
new Dog("Harry")
val cat =
new Cat("Sally")
val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name))
// Prints Harry Sally
Mixins用於進行類組合的特質:
abstract
class A {
val message: String
}
class B
extends A {
val message = "I'm an instance of class B"
}
//
此處的特質
C
即為
mixin
trait C
extends A {
def loudMessage = message.toUpperCase()
}
class D
extends B
with C
val d =
new D
println(d.message)
// I'm an instance of class B
println(d.loudMessage)
// I'M AN INSTANCE OF CLASS B
6 當做富介面使用的特質
//
注意抽象方法和具體方法的結合
trait Logger {
def log(msg: String)
def info(msg: String) { log("INFO: " + msg) }
def warn(msg: String) { log("WARN: " + msg) }
def severe(msg: String) {log("SEVERE: " + msg)}
}
class Account {
protected
var balance:Double = 0
}
class SavingsAccount
extends Account
with Logger {
def withdraw(amount: Double) {
if (amount > balance) severe("Insufficient funds")
else "you can do this" }
override
def log(msg: String) { println(msg) }
}
object test{
def main(args: Array[String]): Unit = {
val acc =
new SavingsAccount
acc.withdraw(100)
}
}
//result
SEVERE: Insufficient funds
7 特質中的具體欄位和抽象欄位
特質中的欄位有初始值則就是具體的,否則是抽象的。
trait ShortLogger
extends Logged {
val maxLength = 15
//
具體欄位
}
那麼繼承該特質的子類是如何獲得這個欄位的呢。Scala是直接將該欄位放入到繼承該特製的子類中,而不是被繼承。例如:
class SavingsAccount
extends Account
with ConsoleLogger
with ShortLogger {
var interest = 0.0
def withdraw(amount: Double) {
if (amount > balance) log("Insufficient funds")
else ...
}
}
特質中的抽象欄位在具體的子類中必須被重寫:
trait ShortLogger
extends Logged {
val maxLength: Int
//
抽象欄位
override
def log(msg: String) {
super.log(
if (msg.length <= maxLength) msg
else msg.substring(0, maxLength - 3) + "...")
}
}
class SavingsAccount
extends Account
with ConsoleLogger
with ShortLogger {
val maxLength = 20
//
不需要寫
override
}
8 特質構造順序
特質也是有構造器的,由欄位的初始化和其他特質體中的語句構成:
trait FileLogger
extends Logger {
val out =
new PrintWriter("app.log")
//
構造器的一部分
out.println("# " +
new Date().toString)
//
也是構造器的一部分
def log(msg: String) { out.println(msg); out.flush() }
}
這些語句在任何混入了該特質的物件在構造時都會被執行。 構造器的順序:
- 首先呼叫超類的構造器
- 特質構造器在超類構造器之後、類構造器之前執行
- 特質由左到右被構造
- 每個特質中,父特質先被構造
- 如果多個特質共有一個父特質,那麼那個父特質已經被構造,則不會被再次構造
- 所有特質構造完畢後,子類被構造。 例如:
class SavingsAccount extends Account with FileLogger with ShortLogger
構造器執行順序:
1Account (超類)
2 Logger (第一個特質的父特質)
3 FileLogger
4 ShortLogger
5 SavingsAccount
9 初始化特質中的欄位
特質不能有構造器引數,每個特質都有一個無參構造器。 這也是特質和類的差別。 例如: 我們要在構造的時候指定log的輸出檔案:
trait FileLogger
extends Logger {
val filename: String
//
構造器一部分
val out =
new PrintWriter(filename)
//
構造器的一部分
def log(msg: String) { out.println(msg); out.flush() }
}
val acct =
new SavingsAccount
extends Account
with FileLogger("myapp.log")
//error
,特質沒有帶引數的構造器
//
你也許會想到和前面重寫
maxLength
一樣,在這裡重寫
filename:
val acct =
new SavingsAccount
with FileLogger {
val filename = "myapp.log"
//
這樣是行不通的
}
FileLogger的構造器先於子類構造器執行。這裡的子類其實是一個擴充套件自SavingsAccount 並混入了FileLogger特質的匿名類。而filename的初始化發生在這個匿名類中,而FileLogger的構造器會先執行,因此new PrintWriter(filename)語句會丟擲一個異常。 解決方法是要麼使用提前定義或者使用懶值:
val acct =
new {
val filename = "myapp.log"
}
with SavingsAccount
with FileLogger
//
對於類同樣:
class SavingsAccount
extends {
val filename = "myapp.log"
}
with Account
with FileLogger {
...
// SavingsAccount
的實現
}
//
或使用
lazy
trait FileLogger
extends Logger {
val filename: String
//
構造器一部分
lazy
val out =
new PrintWriter(filename)
//
構造器的一部分
def log(msg: String) { out.println(msg); out.flush() }
}
10 擴充套件類的特質
特質也可以擴充套件類,這個類將會自動成為所有混入該特質的超類
trait LoggedException extends Exception with Logged {
def log() { log(getMessage()) }
}
log方法呼叫了從Exception超類繼承下來的getMessage 方法。那麼混入該特質的類:
class UnhappyException extends LoggedException {
override def getMessage() = "arggh!"
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913892/viewspace-2660823/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Scala 簡介 [摘自 Scala程式設計 ]程式設計
- Scala學習總結(from scala for the Impatient)
- Awesome Scala
- scala(一)
- Scala - DataFrame
- scala入門之編寫scala指令碼指令碼
- Scala(四):物件物件
- Scala(三):類
- scala(四)集合
- Scala操作Map
- Scala特質
- scala 列舉
- Flink - 安裝包scala 2.12和scala 2.11的區別
- The Coding Kata: FizzBuzzWhizz in Scala
- Scala筆記(一)筆記
- scala(二)-for迴圈
- scala(三)函式函式
- Scala 類和物件物件
- Scala 語法(一)
- Scala陣列操作陣列
- scala中的sealed
- scala怎麼退出
- Scala 的學習
- scala物件導向物件
- 17 scala集合使用
- Intellij IDEA 安裝Scala外掛 + 建立Scala專案(Hello World!)IntelliJIdea
- Scala的安裝以及建立Scala專案的詳細步驟
- scala 專案生成工具
- scala中:: , +:, :+, :::, +++的區別
- Scala Essentials: 字串內插字串
- Kotlin Type? vs Scala OptionKotlin
- 聊聊 scala 的模式匹配模式
- Scala學習筆記筆記
- Scala模式匹配詳解模式
- Scala基礎語法
- Scala常用命令
- Scala基礎學習
- 1.初識scala