文章目錄:
1、類型判斷
有時候,我們設計的程序,要根據變量的類型來執行對應的邏輯。
在scala中,如何來進行類型判斷呢?
有兩種方式:
isInstanceOf
getClass/classOf
isInstanceOf/asInstanceOf
在Java中,可以使用instanceof關鍵字來判斷類型、以及(類型)object來進行類型轉換,在scala中如何實現?
scala中對象提供isInstanceOf和asInstanceOf方法。
isInstanceOf判斷對象是否爲指定類的對象
asInstanceOf將對象轉換爲指定類型
用法
// 判斷對象是否爲指定類型
val trueOrFalse:Boolean = 對象.isInstanceOf[類型]
// 將對象轉換爲指定類型
val 變量 = 對象.asInstanceOf[類型]
示例
示例說明
定義一個Person類
定義一個Student類繼承自Person類
創建一個Student類對象
判斷該對象是否爲Student類型,如果是,將其轉換爲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轉換爲Student3類型
val s2 = s1.asInstanceOf[Student3]
println(s2)
}
}
}
2、getClass和classOf
isInstanceOf 只能判斷對象是否爲指定類以及其子類的對象,而不能精確的判斷出,對象就是指定類的對象。如果要求精確地判斷出對象就是指定類的對象,那麼就只能使用 getClass 和 classOf 。
用法
p.getClass可以精確獲取對象的類型
classOf[x]可以精確獲取類型
使用==操作符可以直接比較類型
示例
示例說明
定義一個Person類
定義一個Student類繼承自Person類
創建一個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
}
3、抽象類
和Java語言一樣,scala中也可以定義抽象類
定義
如果類的某個成員在當前類中的定義是不包含完整的,它就是一個抽象類
不完整定義有兩種情況:
1. 方法沒有方法體(抽象方法)
2. 變量沒有初始化(抽象字段)
定義抽象類和Java一樣,在類前面加上abstract關鍵字
// 定義抽象類
abstract class 抽象類名 {
// 定義抽象字段
val 抽象字段名:類型
// 定義抽象方法
def 方法名(參數:參數類型,參數:參數類型...):返回類型
}
抽象方法
示例
• 設計4個類,表示上述圖中的繼承關係
• 每一個形狀都有自己求面積的方法,但是不同的形狀計算面積的方法不同
步驟
1. 創建一個Shape抽象類,添加一個area抽象方法,用於計算面積
2. 創建一個Square正方形類,繼承自Shape,它有一個邊長的主構造器,並實現計算面積方法
3. 創建一個長方形類,繼承自Shape,它有一個長、寬的主構造器,實現計算面積方法
4. 創建一個圓形類,繼承自Shape,它有一個半徑的主構造器,並實現計算面積方法
5. 編寫main方法,分別創建正方形、長方形、圓形對象,並打印它們的面積
參考代碼
// 創建形狀抽象類
abstract class Shape {
def area:Double
}
// 創建正方形類
class Square(var edge:Double /*邊長*/) extends Shape {
// 實現父類計算面積的方法
override def area: Double = edge * edge
}
// 創建長方形類
class Rectangle(var length:Double /*長*/, var width:Double /*寬*/) extends Shape {
override def area: Double = length * width
}
// 創建圓形類
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)
}
}
4、抽象字段
在scala中,也可以定義抽象的字段。如果一個成員變量是沒有初始化,我們就認爲它是抽象的。
定義
語法
abstract class 抽象類 {
val/var 抽象字段:類型
}
示例
示例說明
1. 創建一個Person抽象類,它有一個String抽象字段WHO_AM_I
2. 創建一個Student類,繼承自Person類,重寫WHO_AM_I字段,初始化爲學生
3. 創建一個Policeman類,繼承自Person類,重寫WHO_AM_I字段,初始化警察
4. 添加main方法,分別創建Student/Policeman的實例,然後分別打印WHO_AM_I
參考代碼
// 定義一個人的抽象類
abstract class Person6 {
// 沒有初始化的val字段就是抽象字段
val WHO_AM_I:String
}
class Student6 extends Person6 {
override val WHO_AM_I: String = "學生"
}
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)
}
}
5、匿名內部類
匿名內部類是沒有名稱的子類,直接用來創建實例對象。Spark的源代碼中有大量使用到匿名內部類。
scala中的匿名內部類使用與Java一致。
定義
語法
val/var 變量名 = new 類/抽象類 {
// 重寫方法
}
示例
示例說明
1. 創建一個Person抽象類,並添加一個sayHello抽象方法
2. 添加main方法,通過創建匿名內部類的方式來實現Person
3. 調用匿名內部類對象的sayHello方法
參考代碼
abstract class Person7 {
def sayHello:Unit
}
object Main7 {
def main(args: Array[String]): Unit = {
// 直接用new來創建一個匿名內部類對象
val p1 = new Person7 {
override def sayHello: Unit = println("我是一個匿名內部類")
}
p1.sayHello
}
}
6、特質(trait)
scala中沒有Java中的接口(interface),替代的概念是——特質
定義
• 特質是scala中代碼複用的基礎單元
• 它可以將方法和字段定義封裝起來,然後添加到類中
• 與類繼承不一樣的是,類繼承要求每個類都只能繼承一個超類,而一個類可以添加任意數量的特質。
• 特質的定義和抽象類的定義很像,但它是使用trait關鍵字
語法
定義特質
trait 名稱 {
// 抽象字段
// 抽象方法
}
繼承特質
class 類 extends 特質1 with 特質2 {
// 字段實現
// 方法實現
}
• 使用extends來繼承trait(scala不論是類還是特質,都是使用extends關鍵字)
• 如果要繼承多個trait,則使用with關鍵字
7、trait作爲接口使用
trait作爲接口使用,與java的接口使用方法一樣。
示例 | 繼承單個trait
示例說明
1. 創建一個Logger特質,添加一個接受一個String類型參數的log抽象方法
2. 創建一個ConsoleLogger類,繼承Logger特質,實現log方法,打印消息
3. 添加main方法,創建ConsoleLogger對象,調用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
示例說明
1. 創建一個MessageSender特質,添加send方法
2. 創建一個MessageReceiver特質,添加receive方法
3. 創建一個MessageWorker實現這兩個特質
4. 在main中調用,分別調用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"發送消息:${msg}")
override def receive(): String = "你好!我叫一個好人!"
}
def main(args: Array[String]): Unit = {
val worker = new MessageWorker
worker.send("hello")
println(worker.receive())
}
示例 | object繼承trait
示例說明
1. 創建一個Logger特質,添加一個log抽象方法
2. 創建一個ConsoleLogger的object,實現LoggerForObject特質,實現log方法,打印消息
3. 編寫main方法,調用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("程序退出!")
}
8、特質 | 定義具體的方法
和類一樣,trait中還可以定義具體的方法
示例
示例說明
1. 定義一個Logger特質,添加log實現方法
2. 定義一個UserService類,實現Logger特質
添加add方法,打印"添加用戶"
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 UserService
userService.add()
}
}
9、trait中定義具體的字段和抽象的字段
定義
在trait中可以定義具體字段和抽象字段
繼承trait的子類自動擁有trait中定義的字段
字段直接被添加到子類中
示例
示例說明
通過trait來實現一個日誌輸出工具,該日誌工具可以自動添加日誌的日期
步驟
1. 創建Logger特質
定義一個SimpleDateFormat字段,用來格式化日期(顯示到時間)
定義一個TYPE抽象字段,用於定義輸出的信息
創建一個log抽象方法,用於輸出日誌
2. 創建ConsoleLogger類,實現TYPE抽象字段和log方法
3. 添加main方法
創建ConsoleLogger類對象
調用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")
}
10、使用trait實現模板模式
要實現以下需求:
實現一個輸出日誌的功能
目前要求輸出到控制檯
將來可能會輸出到文件、輸出到Redis、或者更多的需求
如何實現將來不修改之前的代碼,來擴展現有功能呢?
定義
在一個特質中,具體方法依賴於抽象方法,而抽象方法可以放到繼承trait的子類中實現,這種設計方式也稱爲模板模式
在scala中,trait是可以定義抽象方法,也可以定義具體方法的
trait中定義了一個抽象方法
trait中定義了其他的幾個具體方法,會調用抽象方法
其他實現類可以來實現抽象方法
真正調用trait中具體方法的時候,其實會調用實現類的抽象方法實現
示例
示例說明
編寫一個日誌輸出工具,分別有info、warn、error三個級別的日誌輸出
日誌輸出的方式要求設計爲可擴展的,例如:可以輸出到控制檯、將來也可以擴展輸出到文件、數據庫等
實現步驟
1. 添加一個Logger特質
添加一個log抽象方法
添加一個info、warn、error具體方法,這幾個方法調用log抽象方法
2. 創建ConsoleLogger類,實現Logger特質
3. 添加main方法
創建ConsoleLogger類對象
分別調用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("錯誤日誌")
}
11、對象混入trait
scala中可以將trait混入到對象中,就是將trait中定義的方法、字段添加到一個對象中
定義
語法
val/var 對象名 = new 類 with 特質
示例
• 給一個對象添加一些額外的行爲
步驟
1. 創建一個Logger特質
添加一個log實現方法,打印參數
2. 創建一個UserService類
3. 添加main方法
創建UserService對象,混入Logger特質
調用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("混入的方法")
}
12、trait實現調用鏈模式
我們如果要開發一個支付功能,往往需要執行一系列的驗證才能完成支付。例如:
1. 進行支付簽名校驗
2. 數據合法性校驗
3. ...
如果將來因爲第三方接口支付的調整,需要增加更多的校驗規則,此時如何不修改之前的校驗代碼,來實現擴展呢?
責任鏈模式
trait調用鏈
類繼承了多個trait後,可以依次調用多個trait中的同一個方法,只要讓多個trait中的同一個方法在最後都依次執行super關鍵字即可。類中調用多個tait中都有這個方法時,首先會從最右邊的trait方法開始執行,然後依次往左執行,形成一個調用鏈條。
示例
實現一個模擬支付過程的調用鏈
步驟
1. 定義一個HandlerTrait特質
定義一個具體的handler方法,打印"處理數據..."
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 DataValidHanlderTrait extends HandlerTrait {
override def handle(data:String): Unit = {
println("驗證數據...")
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("準備支付...")
super.handle(data)
}
}
def main(args: Array[String]): Unit = {
val service = new PayService
service.handle("支付參數")
}
// 程序運行輸出如下:
// 準備支付...
// 檢查簽名...
// 驗證數據...
// 處理數據...
trait繼承class
定義
trait也可以繼承class的。特質會將class中的成員都繼承下來。
示例
示例說明
定義一個特質,繼承自一個class
步驟
- 創建一個MyUtils類,定義printMsg方法
- 創建一個Logger特質,繼承自MyUtils,定義log方法
- 創建一個Person類,添加name字段
繼承Logger特質
實現sayHello方法,調用log方法 - 添加main方法,創建一個Person對象,調用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()
}
scala操作就到這,內容很多,想要學好spark的小夥伴們一定要認真的把scala學好,下一階段給大家更新spark內容哦!!!!!