Scala語言學(xué)習(xí)四 (類和對象)

類和對象

scala是支持面向?qū)ο蟮拇锕浚灿蓄惡蛯ο蟮母拍罱虽獭N覀円廊豢梢曰趕cala語言來開發(fā)面向?qū)ο蟮膽?yīng)用程序主经。

創(chuàng)建類和對象

語法

  • 使用class來定義一個類
  • 使用new來創(chuàng)建對象

示例說明

創(chuàng)建一個Person類,并創(chuàng)建它的對象

步驟

  1. 創(chuàng)建一個scala項目成畦,并創(chuàng)建一個Object
  2. 添加main方法
  3. 創(chuàng)建類和對象

實現(xiàn)

  1. 在IDEA中創(chuàng)建項目距芬,并創(chuàng)建一個Object(main方法必須放在Object中)
  2. 添加main方法
  3. 創(chuàng)建一個Person類
  4. 在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來定義成員變量
  • 對象直接使用成員變量名稱來訪問成員變量

示例說明

  1. 定義一個Person類,包含一個姓名和年齡字段

  2. 創(chuàng)建一個名為"張三"核偿、年齡為20歲的對象

  3. 打印對象的名字和年齡

步驟

  1. 創(chuàng)建一個Object诚欠,添加main方法
  2. 創(chuàng)建Person類,添加姓名字段和年齡字段漾岳,并對字段進(jìn)行初始化聂薪,讓scala自動進(jìn)行類型推斷
  3. 在main方法中創(chuàng)建Person類對象,設(shè)置成員變量為"張三"蝗羊、20
  4. 打印對象的名字和年齡
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類型的成員變量仁锯,必須要自己手動初始化

示例說明

  1. 定義一個Person類耀找,包含一個姓名和年齡字段
  2. 創(chuàng)建一個名為"張三"、年齡為20歲的對象
  3. 打印對象的名字和年齡

步驟

  1. 創(chuàng)建一個Object业崖,添加main方法
  2. 創(chuàng)建Person類野芒,添加姓名字段和年齡字段,指定數(shù)據(jù)類型双炕,使用下劃線初始化
  3. 在main方法中創(chuàng)建Person類對象狞悲,設(shè)置成員變量為"張三"、20
  4. 打印對象的名字和年齡
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方法

步驟

  1. 創(chuàng)建一個Object,添加main方法
  2. 創(chuàng)建Customer類站超,添加成員變量荸恕、成員方法
  3. 在main方法中創(chuàng)建Customer類對象,設(shè)置成員變量值(張三死相、男)
  4. 調(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)造代碼塊
}

示例說明

  1. 定義一個Person類耙箍,通過主構(gòu)造器參數(shù)列表定義姓名和年齡字段,并且設(shè)置它們的默認(rèn)值
  2. 在主構(gòu)造器中輸出"調(diào)用主構(gòu)造器"
  3. 創(chuàng)建"張三"對象(姓名為張三酥馍,年齡為20)辩昆,打印對象的姓名和年齡
  4. 創(chuàng)建"空"對象,不給構(gòu)造器傳入任何的參數(shù)旨袒,打印對象的姓名和年齡
  5. 創(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方法
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)
  }
}

類型判斷

1557152463629

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)前類中的定義是不包含完整的仑扑,它就是一個抽象類

不完整定義有兩種情況:

  1. 方法沒有方法體(抽象方法
  2. 變量沒有初始化(抽象字段

示例

1552449400240
  • 設(shè)計4個類览爵,表示上述圖中的繼承關(guān)系
  • 每一個形狀都有自己求面積的方法,但是不同的形狀計算面積的方法不同

步驟

  1. 創(chuàng)建一個Shape抽象類夫壁,添加一個area抽象方法拾枣,用于計算面積
  2. 創(chuàng)建一個Square正方形類,繼承自Shape盒让,它有一個邊長的主構(gòu)造器梅肤,并實現(xiàn)計算面積方法
  3. 創(chuàng)建一個長方形類,繼承自Shape邑茄,它有一個長姨蝴、寬的主構(gòu)造器,實現(xiàn)計算面積方法
  4. 創(chuàng)建一個圓形類肺缕,繼承自Shape左医,它有一個半徑的主構(gòu)造器,并實現(xiàn)計算面積方法
  5. 編寫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)為它是抽象的远豺。

示例說明

  1. 創(chuàng)建一個Person抽象類,它有一個String抽象字段WHO_AM_I
  2. 創(chuàng)建一個Student類坞嘀,繼承自Person類躯护,重寫WHO_AM_I字段,初始化為學(xué)生
  3. 創(chuàng)建一個Policeman類丽涩,繼承自Person類棺滞,重寫WHO_AM_I字段,初始化警察
  4. 添加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)部類锰瘸。

示例說明

  1. 創(chuàng)建一個Person抽象類,并添加一個sayHello抽象方法
  2. 添加main方法昂灵,通過創(chuàng)建匿名內(nèi)部類的方式來實現(xiàn)Person
  3. 調(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

示例說明

  1. 創(chuàng)建一個Logger特質(zhì),添加一個接受一個String類型參數(shù)的log抽象方法
  2. 創(chuàng)建一個ConsoleLogger類含潘,繼承Logger特質(zhì)饲做,實現(xiàn)log方法,打印消息
  3. 添加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

示例說明

  1. 創(chuàng)建一個MessageSender特質(zhì),添加send方法
  2. 創(chuàng)建一個MessageReceiver特質(zhì)漱逸,添加receive方法
  3. 創(chuàng)建一個MessageWorker實現(xiàn)這兩個特質(zhì)
  4. 在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

示例說明

  1. 創(chuàng)建一個Logger特質(zhì),添加一個log抽象方法
  2. 創(chuàng)建一個ConsoleLogger的object袋坑,實現(xiàn)LoggerForObject特質(zhì)仗处,實現(xiàn)log方法,打印消息
  3. 編寫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ì) | 定義具體的方法

示例說明

  1. 定義一個Logger特質(zhì)疆柔,添加log實現(xiàn)方法

  2. 定義一個UserService類咒精,實現(xiàn)Logger特質(zhì)

  3. 添加add方法镶柱,打印"添加用戶"

  4. 添加main方法

  5. 創(chuàng)建UserService對象實例

  6. 調(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中定義的字段
  • 字段直接被添加到子類中

步驟

  1. 創(chuàng)建Logger特質(zhì)
    • 定義一個SimpleDateFormat字段,用來格式化日期(顯示到時間)
    • 定義一個TYPE抽象字段模叙,用于定義輸出的信息
    • 創(chuàng)建一個log抽象方法歇拆,用于輸出日志
  2. 創(chuàng)建ConsoleLogger類,實現(xiàn)TYPE抽象字段和log方法
  3. 添加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)步驟

  1. 添加一個Logger特質(zhì)
    • 添加一個log抽象方法
    • 添加一個info替蛉、warn贯溅、error具體方法拄氯,這幾個方法調(diào)用log抽象方法
  2. 創(chuàng)建ConsoleLogger類,實現(xiàn)Logger特質(zhì)
  3. 添加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

示例說明

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

步驟

  1. 創(chuàng)建一個Logger特質(zhì)
    • 添加一個log實現(xiàn)方法,打印參數(shù)
  2. 創(chuàng)建一個UserService類
  3. 添加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)用鏈

1552535850223

步驟

  1. 定義一個HandlerTrait特質(zhì)
    • 定義一個具體的handler方法鄙麦,打印"處理數(shù)據(jù)..."
  2. 定義一個DataValidHandlerTrait,繼承HandlerTrait特質(zhì)
    • 重寫handler方法镊折,打印"驗證數(shù)據(jù)"
    • 調(diào)用父特質(zhì)的handler方法
  3. 定義一個SignatureValidHandlerTrait胯府,繼承HandlerTrait特質(zhì)
    • 重寫Handler方法
    • 打印"檢查簽名"
    • 調(diào)用父特質(zhì)的handler方法
  4. 創(chuàng)建一個PaymentService類
    • 繼承DataValidHandlerTrait
    • 繼承SignatureValidHandlerTrait
    • 定義pay方法
      • 打印"準(zhǔn)備支付"
      • 調(diào)用父特質(zhì)的handler方法
  5. 添加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)造順序如下:
    1. 執(zhí)行父類的構(gòu)造器
    2. 從左到右依次執(zhí)行trait的構(gòu)造器
    3. 如果trait有父trait影所,先構(gòu)造父trait,如果多個trait有同樣的父trait僚碎,則只初始化一次
    4. 執(zhí)行子類構(gòu)造器

示例說明

  • 定義多個特質(zhì)猴娩,然后用一個類去實現(xiàn)它們
  • 測試trait的構(gòu)造順序

步驟

  1. 創(chuàng)建一個Logger特質(zhì),在構(gòu)造器中打印"執(zhí)行Logger構(gòu)造器!"
  2. 創(chuàng)建一個MyLogger特質(zhì)勺阐,繼承自Logger特質(zhì)卷中,,在構(gòu)造器中打印"執(zhí)行MyLogger構(gòu)造器!"
  3. 創(chuàng)建一個TimeLogger特質(zhì)渊抽,繼承自Logger特質(zhì)蟆豫,在構(gòu)造器中打印"執(zhí)行TimeLogger構(gòu)造器!"
  4. 創(chuàng)建一個Person類,在構(gòu)造器中打印"執(zhí)行Person構(gòu)造器!"
  5. 創(chuàng)建一個Student類懒闷,繼承自Person十减、MyLogger、TimeLogge特質(zhì)愤估,在構(gòu)造器中打印"執(zhí)行Student構(gòu)造器!"
  6. 添加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

步驟

  1. 創(chuàng)建一個MyUtils類蔓榄,定義printMsg方法
  2. 創(chuàng)建一個Logger特質(zhì)闹炉,繼承自MyUtils,定義log方法
  3. 創(chuàng)建一個Person類润樱,添加name字段
    • 繼承Logger特質(zhì)
    • 實現(xiàn)sayHello方法渣触,調(diào)用log方法
  4. 添加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()
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗅钻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子店展,更是在濱河造成了極大的恐慌养篓,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赂蕴,死亡現(xiàn)場離奇詭異柳弄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)概说,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門碧注,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人糖赔,你說我怎么就攤上這事萍丐。” “怎么了放典?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵逝变,是天一觀的道長。 經(jīng)常有香客問我奋构,道長壳影,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任弥臼,我火速辦了婚禮宴咧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘醋火。我一直安慰自己悠汽,他們只是感情好箱吕,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布芥驳。 她就那樣靜靜地躺著,像睡著了一般茬高。 火紅的嫁衣襯著肌膚如雪兆旬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天怎栽,我揣著相機(jī)與錄音丽猬,去河邊找鬼宿饱。 笑死,一個胖子當(dāng)著我的面吹牛脚祟,可吹牛的內(nèi)容都是我干的谬以。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼由桌,長吁一口氣:“原來是場噩夢啊……” “哼为黎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起行您,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤铭乾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后娃循,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炕檩,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年捌斧,在試婚紗的時候發(fā)現(xiàn)自己被綠了笛质。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡捞蚂,死狀恐怖经瓷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洞难,我是刑警寧澤舆吮,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站队贱,受9級特大地震影響色冀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柱嫌,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一锋恬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧编丘,春花似錦与学、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抑片,卻和暖如春卵佛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工截汪, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留疾牲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓衙解,卻偏偏與公主長得像阳柔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蚓峦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353