scala 基本介紹與用法(四)

目錄

 

三十二、繼承

定義語法

示例 | 類繼承

示例 | 單例對象繼承

三十三、override和super

三十四、類型判斷

isInstanceOf/asInstanceOf

getClass和classOf

三十五、抽象類

抽象方法

三十六、抽象字段

三十七、匿名內部類

三十八、特質(trait)

trait作爲接口使用

示例 | 繼承單個trait

示例 | 繼承多個trait

示例 | object繼承trait

三十九、特質 | 定義具體的方法

四十、trait中定義具體的字段和抽象的字段

四十一、使用trait實現模板模式

定義

四十二、對象混入trait

四十三、trait實現調用鏈模式

責任鏈模式

trait調用鏈

四十四、trait繼承class


三十二、繼承

scala語言是支持面向對象編程的, 可以使用scala來實現繼承,通過繼承來減少重複代碼。

定義語法

  • 使用extends關鍵字來實現繼承
  • 可以在子類中定義父類中沒有的字段和方法,或者重寫父類的方法
  • 類和單例對象都可以從某個父類繼承

class/object 子類 extends 父類 {

    ..

}

示例 | 類繼承

  • 定義一個Person類,再定義一個Student類,繼承自Person類

  • 創建一個Student類對象實例,並設置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)
  }
}

示例 | 單例對象繼承

  • 創建一個Student單例對象,讓單例對象繼承示例1中的Person類
  • 設置單例對象的名字爲"張三",調用Student單例對象的getName方法
class Person {
  var name = "super"

  def getName = this.name
}

object Student extends Person

object Main13 {
  def main(args: Array[String]): Unit = {

    println(Student.getName)
  }
}

三十三、overridesuper

類似於Java語言, 在子類中使用override需要來重寫父類的成員,可以使用super來引用父類

  • 子類要覆蓋父類中的一個方法,必須要使用override關鍵字
  • 使用override來重寫一個val字段
  • 使用super關鍵字來訪問父類的成員方法

示例

  1. 定義一個Person類,包含
  2. 姓名字段(不可重新賦值)
  3. 獲取姓名方法
  4. 定義一個Student類
  5. 重寫姓名字段
  6. 重寫獲取姓名方法,返回"hello, " + 姓名
  7. 創建Student對象示例,調用它的getName方法
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中有兩種方式進行類型判斷

  1. isInstanceOf
  2. getClass/classOf

isInstanceOf/asInstanceOf

在Java中,可以使用instanceof關鍵字來判斷類型、以及(類型)object來進行類型轉換,在scala中如何實現?

scala中對象提供isInstanceOf和asInstanceOf方法。

  • isInstanceOf判斷對象是否爲指定類的對象
  • asInstanceOf將對象轉換爲指定類型

用法

// 判斷對象是否爲指定類型

val trueOrFalse:Boolean = 對象.isInstanceOf[類型]

// 將對象轉換爲指定類型

val 變量 = 對象.asInstanceOf[類型]

示例

  1. 定義一個Person類
  2. 定義一個Student類繼承自Person類
  3. 創建一個Student類對象
  4. 判斷該對象是否爲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)
    }

  }
}
 

getClassclassOf

isInstanceOf 只能判斷對象是否爲指定類以及其子類的對象,而不能精確的判斷出,對象就是指定類的對象。如果要求精確地判斷出對象就是指定類的對象,那麼就只能使用 getClass 和 classOf 。

用法

  • p.getClass可以精確獲取對象的類型
  • classOf[x]可以精確獲取類型
  • 使用==操作符可以直接比較類型

示例

  1. 定義一個Person類
  2. 定義一個Student類繼承自Person類
  3. 創建一個Student類對象,並指定它的類型爲Person類型
  4. 測試使用isInstance判斷該對象是否爲Person類型
  5. 測試使用getClass/classOf判斷該對象是否爲Person類型
  6. 測試使用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
  }
}
 

三十五、抽象類

和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)
  }
}

三十六、抽象字段

在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)
  }
}

三十七、匿名內部類

匿名內部類是沒有名稱的子類,直接用來創建實例對象。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
  }
}

三十八、特質(trait)

scala中沒有Java中的接口(interface),替代的概念是——特質

  • 特質是scala中代碼複用的基礎單元
  • 它可以將方法和字段定義封裝起來,然後添加到類中
  • 與類繼承不一樣的是,類繼承要求每個類都只能繼承一個超類,而一個類可以添加任意數量的特質。
  • 特質的定義和抽象類的定義很像,但它是使用trait關鍵字

定義特質

trait 名稱 {

    // 抽象字段

    // 抽象方法

}

繼承特質

class extends 特質1 with 特質2 {

    // 字段實現

    // 方法實現

}

  • 使用extends來繼承trait(scala不論是類還是特質,都是使用extends關鍵字)
  • 如果要繼承多個trait,則使用with關鍵字

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("程序退出!")
}

三十九、特質 | 定義具體的方法

和類一樣,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()
  }
}

四十、trait中定義具體的字段和抽象的字段

  • 在trait中可以定義具體字段和抽象字段
  • 繼承trait的子類自動擁有trait中定義的字段
  • 字段直接被添加到子類中

通過trait來實現一個日誌輸出工具,該日誌工具可以自動添加日誌的日期

  1. 創建Logger特質
  • 定義一個SimpleDateFormat字段,用來格式化日期(顯示到時間)
  • 定義一個TYPE抽象字段,用於定義輸出的信息
  • 創建一個log抽象方法,用於輸出日誌
  1. 創建ConsoleLogger類,實現TYPE抽象字段和log方法
  2. 添加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")
  }

四十一、使用trait實現模板模式

要實現以下需求:

  1. 實現一個輸出日誌的功能
  2. 目前要求輸出到控制檯
  3. 將來可能會輸出到文件、輸出到Redis、或者更多的需求

如何實現將來不修改之前的代碼,來擴展現有功能呢?

定義

在一個特質中,具體方法依賴於抽象方法,而抽象方法可以放到繼承trait的子類中實現,這種設計方式也稱爲模板模式

 

在scala中,trait是可以定義抽象方法,也可以定義具體方法的

  • trait中定義了一個抽象方法
  • trait中定義了其他的幾個具體方法,會調用抽象方法
  • 其他實現類可以來實現抽象方法
  • 真正調用trait中具體方法的時候,其實會調用實現類的抽象方法實現

示例

  • 編寫一個日誌輸出工具,分別有info、warn、error三個級別的日誌輸出
  • 日誌輸出的方式要求設計爲可擴展的,例如:可以輸出到控制檯、將來也可以擴展輸出到文件、數據庫等

實現步驟

  1. 添加一個Logger特質
  • 添加一個log抽象方法
  • 添加一個info、warn、error具體方法,這幾個方法調用log抽象方法
  1. 創建ConsoleLogger類,實現Logger特質
  2. 添加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("錯誤日誌")
  }

四十二、對象混入trait

scala中可以將trait混入到對象中,就是將trait中定義的方法、字段添加到一個對象中

val/var 對象名 = new with 特質

示例

  • 給一個對象添加一些額外的行爲

步驟

  1. 創建一個Logger特質
  • 添加一個log實現方法,打印參數
  1. 創建一個UserService
  2. 添加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("混入的方法")
  }

四十三、trait實現調用鏈模式

我們如果要開發一個支付功能,往往需要執行一系列的驗證才能完成支付。例如:

  1. 進行支付簽名校驗
  2. 數據合法性校驗
  3. ...

如果將來因爲第三方接口支付的調整,需要增加更多的校驗規則,此時如何不修改之前的校驗代碼,來實現擴展呢?

責任鏈模式

 

trait調用鏈

 

類繼承了多個trait後,可以依次調用多個trait中的同一個方法,只要讓多個trait中的同一個方法在最後都依次執行super關鍵字即可。類中調用多個tait中都有這個方法時,首先會從最右邊的trait方法開始執行,然後依次往左執行,形成一個調用鏈條。

示例

實現一個模擬支付過程的調用鏈

 

步驟

1.定義一個HandlerTrait特質

  • 定義一個具體的handler方法,打印"處理數據..."

2.定義一個DataValidHandlerTrait,繼承HandlerTrait特質

  • 重寫handler方法,打印"驗證數據"
  • 調用父特質的handler方法

3.定義一個SignatureValidHandlerTrait,繼承HandlerTrait特質

  1. 重寫Handler方法
  2. 打印"檢查簽名"
  3. 調用父特質的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

步驟

1.創建一個MyUtils類,定義printMsg方法

2.創建一個Logger特質,繼承自MyUtils,定義log方法

3.創建一個Person類,添加name字段

  • 繼承Logger特質
  • 實現sayHello方法,調用log方法

4.添加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()
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章