scala_繼承、型別判斷、抽象類、匿名內部類

Imflash發表於2019-09-21

繼承

scala語言是支援物件導向程式設計的,我們也可以使用scala來實現繼承,通過繼承來減少重複程式碼。

定義語法

  • scala和Java一樣,使用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)
  }
}

override和super

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

用法

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

示例

示例說明

  • 定義一個Person類,包含
    • 姓名欄位(不可重新賦值)
    • 獲取姓名方法
  • 定義一個Student類
    • 重寫姓名欄位
    • 重寫獲取姓名方法,返回"hello, " + 姓名
  • 建立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中,如何來進行型別判斷呢?

有兩種方式:

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

  }
}

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

抽象類

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

相關文章