Scala 系列(八)—— 類和對象

1、初識類和對象

Scala 的類與 Java 的類具備很是多的類似性,示例以下:java

// 1. 在 scala 中,類不須要用 public 聲明,全部的類都具備公共的可見性
class Person {

  // 2. 聲明私有變量,用 var 修飾的變量默認擁有 getter/setter 屬性
  private var age = 0

  // 3.若是聲明的變量不須要進行初始賦值,此時 Scala 就沒法進行類型推斷,因此須要顯式指明類型
  private var name: String = _


  // 4. 定義方法,應指明傳參類型。返回值類型不是必須的,Scala 能夠自動推斷出來,可是爲了方便調用者,建議指明
  def growUp(step: Int): Unit = {
    age += step
  }

  // 5.對於改值器方法 (即改變對象狀態的方法),即便不須要傳入參數,也建議在聲明中包含 ()
  def growUpFix(): Unit = {
    age += 10
  }

  // 6.對於取值器方法 (即不會改變對象狀態的方法),沒必要在聲明中包含 ()
  def currentAge: Int = {
    age
  }

  /**
   * 7.不建議使用 return 關鍵字,默認方法中最後一行代碼的計算結果爲返回值
   *   若是方法很簡短,甚至能夠寫在同一行中
   */
  def getName: String = name

}


// 伴生對象
object Person {

  def main(args: Array[String]): Unit = {
    // 8.建立類的實例
    val counter = new Person()
    // 9.用 var 修飾的變量默認擁有 getter/setter 屬性,能夠直接對其進行賦值
    counter.age = 12
    counter.growUp(8)
    counter.growUpFix()
    // 10.用 var 修飾的變量默認擁有 getter/setter 屬性,能夠直接對其進行取值,輸出: 30
    println(counter.age)
    // 輸出: 30
    println(counter.currentAge)
    // 輸出: null
    println(counter.getName)
  }

}


2、類

2.1 成員變量可見性

Scala 中成員變量的可見性默認都是 public,若是想要保證其不被外部干擾,能夠聲明爲 private,並經過 getter 和 setter 方法進行訪問。git

2.2 getter和setter屬性

getter 和 setter 屬性與聲明變量時使用的關鍵字有關:github

  • 使用 var 關鍵字:變量同時擁有 getter 和 setter 屬性;
  • 使用 val 關鍵字:變量只擁有 getter 屬性;
  • 使用 private[this]:變量既沒有 getter 屬性、也沒有 setter 屬性,只能經過內部的方法訪問;

須要特別說明的是:假設變量名爲 age,則其對應的 get 和 set 的方法名分別叫作 ageage_=shell

class Person {

  private val name = "heibaiying"
  private var age = 12
  private[this] var birthday = "2019-08-08"
  // birthday 只能被內部方法所訪問
  def getBirthday: String = birthday
}

object Person {
  def main(args: Array[String]): Unit = {
    val person = new Person
    person.age = 30
    println(person.name)
    println(person.age)
    println(person.getBirthday)
  }
}

解釋說明:編程

示例代碼中 person.age=30 在執行時內部實際是調用了方法 person.age_=(30),而 person.age 內部執行時實際是調用了 person.age() 方法。想要證實這一點,能夠對代碼進行反編譯。同時爲了說明成員變量可見性的問題,咱們對下面這段代碼進行反編譯:ide

class Person {
var name = ""
private var age = ""
}

依次執行下面編譯命令:函數式編程

> scalac Person.scala
> javap -private Person

編譯結果以下,從編譯結果能夠看到實際的 get 和 set 的方法名 (由於 JVM 不容許在方法名中出現=,因此它被翻譯成$eq),同時也驗證了成員變量默認的可見性爲 public。函數

Compiled from "Person.scala"
public class Person {
private java.lang.String name;
private java.lang.String age;
 
public java.lang.String name();
public void name_$eq(java.lang.String);
 
private java.lang.String age();
private void age_$eq(java.lang.String);
 
public Person();
}

2.3 @BeanProperty

在上面的例子中能夠看到咱們是使用 . 來對成員變量進行訪問的,若是想要額外生成和 Java 中同樣的 getXXX 和 setXXX 方法,則須要使用@BeanProperty 進行註解。工具

class Person {
  @BeanProperty var name = ""
}

object Person {
  def main(args: Array[String]): Unit = {
    val person = new Person
    person.setName("heibaiying")
    println(person.getName)
  }
}

2.4 主構造器

和 Java 不一樣的是,Scala 類的主構造器直接寫在類名後面,但注意如下兩點:測試

  • 主構造器傳入的參數默認就是 val 類型的,即不可變,你沒有辦法在內部改變傳參;
  • 寫在主構造器中的代碼塊會在類初始化的時候被執行,功能相似於 Java 的靜態代碼塊 static{}
class Person(val name: String, val age: Int) {

  println("功能相似於 Java 的靜態代碼塊 static{}")

  def getDetail: String = {
    //name="heibai" 沒法經過編譯
    name + ":" + age
  }
}

object Person {
  def main(args: Array[String]): Unit = {
    val person = new Person("heibaiying", 20)
    println(person.getDetail)
  }
}

輸出:
功能相似於 Java 的靜態代碼塊 static{}
heibaiying:20

2.5 輔助構造器

輔助構造器有兩點硬性要求:

  • 輔助構造器的名稱必須爲 this;
  • 每一個輔助構造器必須以主構造器或其餘的輔助構造器的調用開始。
class Person(val name: String, val age: Int) {

  private var birthday = ""

  // 1.輔助構造器的名稱必須爲 this
  def this(name: String, age: Int, birthday: String) {
    // 2.每一個輔助構造器必須以主構造器或其餘的輔助構造器的調用開始
    this(name, age)
    this.birthday = birthday
  }

  // 3.重寫 toString 方法
  override def toString: String = name + ":" + age + ":" + birthday
}

object Person {
  def main(args: Array[String]): Unit = {
    println(new Person("heibaiying", 20, "2019-02-21"))
  }
}

2.6 方法傳參不可變

在 Scala 中,方法傳參默認是 val 類型,即不可變,這意味着你在方法體內部不能改變傳入的參數。這和 Scala 的設計理念有關,Scala 遵循函數式編程理念,強調方法不該該有反作用。

class Person() {

  def low(word: String): String = {
    word="word" // 編譯沒法經過
    word.toLowerCase
  }
}


3、對象

Scala 中的 object(對象) 主要有如下幾個做用:

  • 由於 object 中的變量和方法都是靜態的,因此能夠用於存放工具類;
  • 能夠做爲單例對象的容器;
  • 能夠做爲類的伴生對象;
  • 能夠拓展類或特質;
  • 能夠拓展 Enumeration 來實現枚舉。

3.1 工具類&單例&全局靜態常量&拓展特質

這裏咱們建立一個對象 Utils,代碼以下:

object Utils {

  /*
   *1. 至關於 Java 中的靜態代碼塊 static,會在對象初始化時候被執行
   *   這種方式實現的單例模式是餓漢式單例,即不管你的單例對象是否被用到,
   *   都在一開始被初始化完成
   */
  val person = new Person
  
  // 2. 全局固定常量 等價於 Java 的 public static final 
  val CONSTANT = "固定常量"

  // 3. 全局靜態方法
  def low(word: String): String = {
    word.toLowerCase
  }
}

其中 Person 類代碼以下:

class Person() {
  println("Person 默認構造器被調用")
}

新建測試類:

// 1.ScalaApp 對象擴展自 trait App
object ScalaApp extends App {

  // 2.驗證單例
  println(Utils.person == Utils.person)

  // 3.獲取全局常量
  println(Utils.CONSTANT)

  // 4.調用工具類
  println(Utils.low("ABCDEFG"))
  
}

// 輸出以下:
Person 默認構造器被調用
true
固定常量
abcdefg

3.2 伴生對象

在 Java 中,你一般會用到既有實例方法又有靜態方法的類,在 Scala 中,能夠經過類和與類同名的伴生對象來實現。類和伴生對象必須存在與同一個文件中。

class Person() {

  private val name = "HEIBAIYING"

  def getName: String = {
    // 調用伴生對象的方法和屬性
    Person.toLow(Person.PREFIX + name)
  }
}

// 伴生對象
object Person {

  val PREFIX = "prefix-"

  def toLow(word: String): String = {
    word.toLowerCase
  }

  def main(args: Array[String]): Unit = {
    val person = new Person
    // 輸出 prefix-heibaiying  
    println(person.getName)
  }

}

3.3 實現枚舉類

Scala 中沒有直接提供枚舉類,須要經過擴展 Enumeration,並調用其中的 Value 方法對全部枚舉值進行初始化來實現。

object Color extends Enumeration {

  // 1.類型別名,建議聲明,在 import 時有用
  type Color = Value

  // 2.調用 Value 方法
  val GREEN = Value
  // 3.只傳入 id
  val RED = Value(3)
  // 4.只傳入值
  val BULE = Value("blue")
  // 5.傳入 id 和值
  val YELLOW = Value(5, "yellow")
  // 6. 不傳入 id 時,id 爲上一個聲明變量的 id+1,值默認和變量名相同
  val PINK = Value
 
}

使用枚舉類:

// 1.使用類型別名導入枚舉類
import com.heibaiying.Color.Color

object ScalaApp extends App {

  // 2.使用枚舉類型,這種狀況下須要導入枚舉類
  def printColor(color: Color): Unit = {
    println(color.toString)
  }

  // 3.判斷傳入值和枚舉值是否相等
  println(Color.YELLOW.toString == "yellow")
  // 4.遍歷枚舉類和值
  for (c <- Color.values) println(c.id + ":" + c.toString)
}

//輸出
true
0:GREEN
3:RED
4:blue
5:yellow
6:PINK


參考資料

  1. Martin Odersky . Scala 編程 (第 3 版)[M] . 電子工業出版社 . 2018-1-1
  2. 凱.S.霍斯特曼 . 快學 Scala(第 2 版)[M] . 電子工業出版社 . 2017-7

更多大數據系列文章能夠參見 GitHub 開源項目大數據入門指南

相關文章
相關標籤/搜索