scala-繼承與特質(zhì)

繼承與特質(zhì)

繼承

繼承的概念

使用extends關(guān)鍵字:

package hhb.cn.part05

class person(name: String, age: Int)

//默認(rèn)的類構(gòu)造器屬性是val,只有g(shù)etter方法,stuNo使用var修飾,所以有g(shù)etter setter方法
class Student(name: String, age: Int, var stuNo: String) extends person(name, age)

object ExtendsDemo {

  def main(args: Array[String]): Unit = {
    val student = new Student("張三", 20, "1001")
    student.stuNo = "1002"

    println(student.stuNo)

  }
}

構(gòu)造器的執(zhí)行順序

Scala在繼承的時(shí)候構(gòu)造器的執(zhí)行順序:首先執(zhí)行父類的主構(gòu)造器,其次執(zhí)行子類自身的主構(gòu)造器。

類有一個(gè)主構(gòu)造器和任意數(shù)量的輔助構(gòu)造器,而每個(gè)輔助構(gòu)造器都必須以對(duì)先前定義的輔助構(gòu)造器或主構(gòu)造器的調(diào)用開始。

子類的輔助構(gòu)造器最終都會(huì)調(diào)用主構(gòu)造器。只有主構(gòu)造器可以調(diào)用父類的構(gòu)造器。

package hhb.cn.part05

class person(name: String, age: Int) {
  println("這是父類構(gòu)造器!")
}

//默認(rèn)的類構(gòu)造器屬性是val,只有g(shù)etter方法,stuNo使用var修飾,所以有g(shù)etter setter方法
class Student(name: String, age: Int, var stuNo: String) extends person(name, age) {
  println("這是子類構(gòu)造器")
}

object ExtendsDemo {

  def main(args: Array[String]): Unit = {
    val student = new Student("張三", 20, "1001")
    student.stuNo = "1002"

    println(student.stuNo)
  }
}

輸出:

這是父類構(gòu)造器!
這是子類構(gòu)造器
1002

override方法重寫

Scala在繼承的時(shí)候構(gòu)造器的執(zhí)行順序:首先執(zhí)行父類的主構(gòu)造器,其次執(zhí)行子類自身的主構(gòu)造器。

類有一個(gè)主構(gòu)造器和任意數(shù)量的輔助構(gòu)造器,而每個(gè)輔助構(gòu)造器都必須以對(duì)先前定義的輔助構(gòu)造器或主構(gòu)造器的調(diào)用開始。

子類的輔助構(gòu)造器最終都會(huì)調(diào)用主構(gòu)造器。只有主構(gòu)造器可以調(diào)用父類的構(gòu)造器。

package hhb.cn.part05


class Programmer(name: String, age: Int) {

  def coding: Unit = {
    println("我在寫代碼")
  }
}

class ScalaProgrammer(name: String, age: Int, workNo: String) extends Programmer(name, age) {

  override def coding: Unit = {
    //調(diào)用父類的方法
    super.coding

    //增加自己的實(shí)現(xiàn)
    println("我在用Scala寫代碼")
  }
}


object OverrideDemo {

  def main(args: Array[String]): Unit = {
    val scalaProgrammer = new ScalaProgrammer("張三", 20, "1001")
    scalaProgrammer.coding
  }

}

需要強(qiáng)調(diào)一點(diǎn):如果父類是抽象類,則override關(guān)鍵字可以不加。如果繼承的父類是抽象類(假設(shè)抽象類為AbstractClass,子類為SubClass),在SubClass類中,AbstractClass對(duì)應(yīng)的抽象方法如果沒有實(shí)現(xiàn)的話,那SubClass也必須定義為抽象類,否則的話必須要有方法的實(shí)現(xiàn)。

package hhb.cn.part05

abstract class Person(name: String, age: Int) {
  def talk: Unit
}

class Teacher(name: String, age: Int, workNo: String) extends Person(name, age) {
  //重寫抽象類的walk方法,可以不加override方法
  def talk: Unit = {
    println("重寫父類的方法")
  }
}

object AbstractDemo {

  def main(args: Array[String]): Unit = {
    val teacher = new Teacher("張三", 20, "123")
    teacher.talk
  }
}

類型檢查與轉(zhuǎn)換

要測試某個(gè)對(duì)象是否屬于某個(gè)給定的類,可以用isInstanceOf方法。如果測試成功,可以用asInstanceOf方法進(jìn)行類型轉(zhuǎn)換。

if(p.isInstanceOf[Employee]){
  //s的類型轉(zhuǎn)換為Employee
  val s = p.asInstanceOf[Employee]
}

如果p指向的是Employee類及其子類的對(duì)象,則p.isInstanceOf[Employee]將會(huì)成功。

如果p是null,則p.isInstanceOf[Employee]將返回false,且p.asInstanceOf[Employee]將返回null。

如果p不是一個(gè)Employee,則p.asInstanceOf[Employee]將拋出異常。

如果想要測試p指向的是一個(gè)Employee對(duì)象但又不是其子類,可以用:

if(p.getClass == classOf[Employee])

classOf方法定義在scala.Preder對(duì)象中,因此會(huì)被自動(dòng)引入。

不過,與類型檢查和轉(zhuǎn)換相比,模式匹配通常是更好的選擇。

p match{
  //將s作為Employee處理
  case s: Employee => ...
  //p不是Employee的情況
  case _ => ....
}

示例:

package hhb.cn.part05


class Person2

class Student2 extends Person2


object InstanceDemo {

  def main(args: Array[String]): Unit = {
    val p: Person2 = new Student2
    var s: Student2 = null
    //如果對(duì)象s 是一個(gè)null,isInstanceOf返回是false
    println(s.isInstanceOf[Student2])

    // 判斷對(duì)象p是不是Student2這個(gè)類型
    if (p.isInstanceOf[Student2]) { // true
      //把p這個(gè)對(duì)象轉(zhuǎn)化為Student2類型后賦值給s
      s = p.asInstanceOf[Student2]
    }
    // 輸出s是否是Student2
    println(s.isInstanceOf[Student2]) // true

    //此時(shí)p已經(jīng)是Student2類型
    println(p.getClass == classOf[Person2]) //false
    println(p.getClass == classOf[Student2]) // true

    println("-================================")

    p match {
      case s: Student2 => println("它是Student2類型的對(duì)象")
      case _ => println("它什么也不是")
    }
  }
}

特質(zhì)

作為接口使用的特質(zhì)

Scala中的trait特質(zhì)是一種特殊的概念。

首先可以將trait作為接口來使用,此時(shí)的trait就與Java中的接口非常類似。

在trait中可以定義抽象方法,與抽象類中的抽象方法一樣,只要不給出方法的具體實(shí)現(xiàn)即可。

類可以使用extends關(guān)鍵字繼承trait。

注意:在Scala中沒有implement的概念,無論繼承類還是trait特質(zhì),統(tǒng)一都是extends。

類繼承trait特質(zhì)后,必須實(shí)現(xiàn)其中的抽象方法,實(shí)現(xiàn)時(shí)可以省略override關(guān)鍵字

Scala不支持對(duì)類進(jìn)行多繼承,但是支持多重繼承trait特質(zhì),使用with關(guān)鍵字即可。

package hhb.cn.part06

trait HelloTrait {
  def seyHello: String
}

trait MakeFriendTrait {
  def makerFriend
}

class Person(name: String) extends HelloTrait with MakeFriendTrait {
  override def seyHello: String = {
    println(s"Hello,My Name is $name")
    name
  }

  override def makerFriend: Unit = {
    println(s"Hi, $name")
  }
}

//如果一個(gè)類繼承了多個(gè)trait,第一個(gè)Trait用extends,其他的Trait用with關(guān)鍵字
object TraitDemo extends App {
  val person = new Person("zhangsan")
  println(person.seyHello)
  person.makerFriend
}

帶有具體實(shí)現(xiàn)的特質(zhì)

具體方法

Scala中的trait特質(zhì)不僅僅可以定義抽象方法,還可以定義具體實(shí)現(xiàn)的方法,這時(shí)的trait更像是包含了通用工具方法的類。比如,trait中可以包含一些很多類都通用的功能方法,比如打印日志等等,Spark中就使用了trait來定義通用的日志打印方法。

具體字段

Scala trait特質(zhì)中的字段可以是抽象的,也可以是具體的。

package hhb.cn.part06

trait People {
  val name: String
  val age = 30
  val score: Double = 100

  def eat: Unit = {
    println("Eating........")
  }

  def test
}

trait Worker {
  val age = 20

  def work: Unit = {
    println("Working.........")
  }

}

class Student extends People with Worker {
  //重寫抽象字段name,此處override可以省略
  override val name: String = "lisi"
  //由于Worker和People中都有age字段,所以當(dāng)繼承這兩個(gè)類需要重寫age字段,此時(shí)override關(guān)鍵字不能省略,否則會(huì)報(bào)錯(cuò)
  override val age: Int = 25

  override def test: Unit = {
    println("============")
  }
}

object TraitDemoTwo extends App {

  val student = new Student
  student.eat
  student.work
  println(student.name)
  println(student.age)
  println(student.score)
  student.test
}

注意:特質(zhì)Person和Worker中都有age字段,當(dāng)Student繼承這兩個(gè)特質(zhì)時(shí),需要重寫age字段,并且要用override關(guān)鍵字,否則就會(huì)報(bào)錯(cuò)。

特質(zhì)構(gòu)造順序

在Scala中,trait特質(zhì)也是有構(gòu)造器的,也就是trait中的不包含在任何方法中的代碼。

構(gòu)造器以如下順序執(zhí)行:

  1. 執(zhí)行父類的構(gòu)造器;

  2. 執(zhí)行trait的構(gòu)造器,多個(gè)trait從左到右依次執(zhí)行;

  3. 構(gòu)造trait時(shí)會(huì)先構(gòu)造父trait,如果多個(gè)trait繼承同一個(gè)父trait,則父trait只會(huì)構(gòu)造一次;

  4. 所有trait構(gòu)造完畢之后,子類的構(gòu)造器才執(zhí)行

package hhb.cn.part06


class Person2 {
  println("Person's Constructor!")
}

trait Logger {
  println("Logger's Constructor!")
}

trait MyLogger extends Logger {
  println("MyLogger's Constructor!")

}

trait TimeLogger extends Logger {
  println("TimeLogger's Constructor!")

}

//如果一個(gè)類繼承了父類,也繼承了特質(zhì),要先寫父類,在寫特質(zhì)
// extends 父類 with 特質(zhì)
class Student2 extends Person2 with MyLogger with Logger with TimeLogger {
  println("Student2======")
}


object TraitDemoThree extends App {
  val student = new Student2
}

執(zhí)行結(jié)果

Person's Constructor!
Logger's Constructor!
MyLogger's Constructor!
TimeLogger's Constructor!
Student2======

特質(zhì)繼承類

在Scala中,trait特質(zhì)也可以繼承class類,此時(shí)這個(gè)class類就會(huì)成為所有繼承此trait的類的父類。

package hhb.cn.part06

class MyUtil {
  def printMessage(name: String): Unit = {
    println("Hello, " + name)
  }
}

trait Log extends MyUtil {
  def sayHi(message: String): Unit = {
    println("Hi, " + message)
  }
}

// MyStudent 繼承了Log特質(zhì),Log特質(zhì)繼承了MyUtil類,那么MyUtil就成了MyStudent 的父類,繼承的傳遞性
class MyStudent(name: String) extends Log {
  def log(): Unit = {
    println("MyStudent:" + name)
    printMessage(name)
    sayHi(name)
  }
}

object TraitDemoFour {
  def main(args: Array[String]): Unit = {
    val student = new MyStudent("zhangsan")
    student.log()
  }
}

使用 trait 實(shí)現(xiàn)適配器模式

當(dāng)某個(gè)特質(zhì)中有多個(gè)抽象方法,而我們只需要用到某個(gè)或某幾個(gè)方法時(shí)不得不將該特質(zhì)所有抽象方法重寫。針對(duì)這種情況可以定義一個(gè)抽象類繼承該特質(zhì),重寫特質(zhì)的所有方法,方法體為空。需要使用哪個(gè)方法只需要定義類繼承抽象類,重寫指定方法即可。

package hhb.bilibili.oop

/**
 * @description:
 * @date: 2020-10-19 21:38
 **/
object ClassDemo20 {

  trait LOL {
    def top()

    def mid()

    def adc()

    def support()
  }

  abstract class Player extends LOL {
    override def top(): Unit = {

    }

    override def mid(): Unit = {

    }

    override def adc(): Unit = {

    }

    override def support(): Unit = {

    }
  }

  class GreenHand extends Player {
    override def support(): Unit = {
      println("輔助")
    }
  }

  //Player 就是適配器類
  def main(args: Array[String]): Unit = {
    val hand = new GreenHand
    hand.support()
  }

}

使用 trait 實(shí)現(xiàn)模板方法模式

在 Scala 中我們可以先定義一個(gè)操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該結(jié)構(gòu)的情況下重定義該算法的某些特定步驟,這就是模板方法設(shè)計(jì)模式。

優(yōu)點(diǎn):擴(kuò)展性強(qiáng)、符號(hào)開閉原則。

缺點(diǎn):類的個(gè)數(shù)增加會(huì)導(dǎo)致系統(tǒng)龐大,設(shè)計(jì)更抽象、增加代碼閱讀難度。

package hhb.bilibili.oop

/**
 * @description:
 * @date: 2020-10-19 21:45
 **/
object ClassDemo21 {

  abstract class Template {
    def code()

    def getRunTime(): Unit = {
      val l1 = System.currentTimeMillis()
      code()
      val l2 = System.currentTimeMillis()
      println(l2 - l1)
    }
  }

  class ForDemo extends Template {
    override def code(): Unit = for (i <- 1 to 10000) println("hello Scala")
  }

  def main(args: Array[String]): Unit = {
    val demo = new ForDemo
    demo.getRunTime()
  }
}

使用 trait 實(shí)現(xiàn)責(zé)任鏈模式

多個(gè) trait 中出現(xiàn)了同一方法,且該方法最后都調(diào)用了 super.該方法名(),當(dāng)類繼承了這多個(gè) trait 后就可以依次調(diào)用多個(gè) trait 中的此同一個(gè)方法了,這就形成了一個(gè)調(diào)用鏈。

執(zhí)行順序:從右至左、先子類后父類。

package hhb.bilibili.oop

/**
 * @description:
 * @author: huanghongbo
 * @date: 2020-10-19 22:03
 **/
object ClassDemo22 {

  trait Handle {
    def handler(data: String): Unit = {
      println("具體要處理的數(shù)據(jù)。。。4")
      println(data) // 5
    }
  }

  trait DataValidHandle extends Handle {
    override def handler(data: String): Unit = {
      println("驗(yàn)證數(shù)據(jù) 3")
      //核心:調(diào)用父特質(zhì)的handle方法
      super.handler(data)
    }
  }

  trait SignHandle extends Handle {
    override def handler(data: String): Unit = {
      println("驗(yàn)簽數(shù)據(jù) 2")
      //核心:調(diào)用父特質(zhì)的handle方法
      super.handler(data)
    }
  }

  // 疊加特質(zhì)
  class Payment extends DataValidHandle with SignHandle {
    override def handler(data: String): Unit = {
      println("用戶發(fā)起支付請求 1")
      super.handler(data)
    }
  }

  /**
   * 執(zhí)行順序:
   * 1. 。按照從右向左的順序,從右至左、先子類后父類。
   * @param args
   */
  def main(args: Array[String]): Unit = {
    val payment = new Payment
    payment.handler("123")
  }

}

Ordered和Ordering

在Java中對(duì)象的比較有兩個(gè)接口,分別是Comparable和Comparator。它們之間的區(qū)別在于:

  • 實(shí)現(xiàn)Comparable接口的類,重寫compareTo()方法后,其對(duì)象自身就具有了可比較性;

  • 實(shí)現(xiàn)Comparator接口的類,重寫了compare()方法后,則提供一個(gè)第三方比較器,用于比較兩個(gè)對(duì)象。

在Scala中也引入了以上兩種比較方法(Scala.math包下):

Ordered特質(zhì)混入Java的Comparable接口,它定義了相同類型間的比較方式,但這種內(nèi)部比較方式是單一的;

trait Ordered[A] extends Any with java.lang.Comparable[A]{......}

Ordering特質(zhì)混入Comparator接口,它是提供第三方比較器,可以自定義多種比較方式,在實(shí)際開發(fā)中也是使用比較多的,靈活解耦合。

trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable {......}

排序操作

package hhb.cn.part06

import scala.util.Sorting


case class Project(tag: String, score: Int) extends Ordered[Project] {

  override def compare(that: Project): Int = {
    tag.compareTo(that.tag)
  }
}

object OrderDemo {

  def main(args: Array[String]): Unit = {
    //字典序
    val list = List(Project("hadoop", 40), Project("flink", 90), Project("hive", 30), Project("kafka", 20))
    println(list.sorted)


    val tuples = Array(("a", 3, 'b'), ("b", 1, 'c'), ("c", 2, 'a'))
    //默認(rèn)使用第一個(gè)字母排序
    Sorting.quickSort(tuples)
    println(tuples.toBuffer)

    //Ordering.by[(String, Int, Char), Int](_._2) 將一個(gè)(String, Int, Char)
    // 類型的Tuple元組轉(zhuǎn)換成Int類型,并根據(jù)Tuple元組中第二個(gè)元素排序
    Sorting.quickSort(tuples)(Ordering.by[(String, Int, Char), Int](_._2))
    println(tuples.toBuffer)
  }
}
第五個(gè)模塊錯(cuò)題集.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容