1. 程式人生 > >Scala中的面向物件

Scala中的面向物件

作者:林偉兵,叩丁狼高階講師。本文為原創文章,轉載請註明出處。  

 

5. 面向物件

5.1 類的定義[屬性和方法]

可以用class來宣告一個類,並用new關鍵字來建立一個物件。

對於類中的全域性變數,必須在宣告的時候指定其預設值,否則就會報錯。

同時可以在類中定義一系列的方法,方法的定義用def 方法名(引數) :返回值 = {方法體}

class Person {

  var name:String = ""
  var age:Int = 0

  def printUserInfo(): Unit ={
    println("name = "+name , "age = "+age)
  }

  def eat(food: String): String = s"$name is eating $food"

}

object Test{

  def main(args: Array[String]): Unit = {
    val person = new Person()
    person.printUserInfo()
    person.name = "張三"
    person.age = 25
    println(person.eat("大閘蟹"))
  }

}

對於全域性變數,可以用 _ 來表示預設初始值,如下:

class SimpleClass {
    var a:Int = _
    var b:Double = _
    var c:Float = _
    var d:Long = _
    var e:String = _
    //對於未知型別的資料使用預設值會直接報錯,因為無法進行型別推導
    //var f = _

    //SimpleClass(0, 0.0, 0.0, 0, null)
    override def toString = s"SimpleClass($a, $b, $c, $d, $e)"
}

有時候為了讓全域性變數私有,我們可以新增 private[this]

 :

class Cat{

  private[this] var name:String = "Lucy"

  def printName(): Unit ={
    println(s"cat name is $name")
  }
}

#呼叫
var cat = new Cat()
cat.printName
//cat.name  這裡獲取不到name這個全域性變數

5.2 主構造器和輔助構造器

Scala的構造器和Java的構造器完全不同,Scala的構造器分為主構造器和輔助構造器;

​ 主構造器的引數列表直接寫在類名(Class)後面,每個類都有一個主構造器(預設是無參構造器)。主構造器與類的定義交織在一起,除了定義的全域性變數和方法,其他都是主構造器的內容,程式碼如下:

class Teacher(name: String) {

  println("teacher start")

  var teachAge: Int = 8
  var height:Double = _
  val school:String = "廣工"

  println("teacher end")

  //輔助構造器,第一行必需呼叫主構造器或者其他已存在的輔助構造器
  //並且只能呼叫在自己之前定義的其他輔助構造器,而不能呼叫後面定義的輔助構造器(避免死迴圈呼叫)
  //引數不可以用val或var來修飾
  def this(name: String, teachAge: Int) {
    this(name)
    this.teachAge = teachAge
  }

  def this(name: String, teachAge: Int, height: Double) {
    this(name,teachAge)
    this.height = height
  }

  def teach(): Unit ={
    println(s"$name is teaching ...")
  }

}

//如果將一個主構造器私有,那麼客戶端是無法獲取該物件的
class Student private(name: String)

object ConstructorDemo {
  def main(args: Array[String]): Unit = {
    val t1 = new Teacher("孔子")
    println(s"teach age is ${t1.teachAge}")

    var t2 = new Teacher("老子", 5)
    println(s"teach age is ${t2.teachAge}")

    var t3 = new Teacher("孫子", 3,1.65d)
    println(s"teach age is ${t3.teachAge}")
  }
}

5.3 類的繼承

  1. 一個類如果想繼承其他的類,就必須呼叫其構造器,無論主構造器還是輔助構造器;在例項化該類的時候,首先會呼叫父類的構造器,再呼叫本身的構造器。

  2. 構造器傳入的引數預設為val型別的資料,類的內部無法修改變數值,只能獲取。

class SeniorTeacher(name: String, desc: String,teachAge: Int) extends Teacher(name, teachAge) {
  println("SeniorTeacher start")
  println(desc)
  //desc = ""

  //被重寫的屬性必須是val修飾的屬性
  override val school:String = "北大"

  override def teach(): Unit ={
    println(s"$name SeniorTeacher is teaching ...")
  }

  println("SeniorTeacher end")
}

val t1 = new SeniorTeacher("小孔", "教齡18",18)
println(t1.school)
t1.teach

5.4 抽象類的使用

抽象函式的宣告:

  • 抽象函式必須使用abstract修飾;
  • 抽象函式的全域性變數可賦值也可不賦值;
  • 抽象函式可有有一個或多個方法未實現;

繼承抽象函式:

  • 繼承抽象函式時,沒有賦值的屬性必須賦值,沒有實現的方法必須實現
  • 在實現屬性/方法的時候可以使用override關鍵字,但是不使用也可以。
  • 被繼承的抽象類屬性不能使用var來修飾,這樣做的好處是防止父類修改子類重寫的屬性。
abstract class Order {

  val oid: Long
  val name: String
  val price: Double

  def orderInfo

}

class WaitPayOrder extends Order {
  override val oid: Long = 100001L
  override val name: String = ""
  override val price: Double = 100.0d

  override def orderInfo: Unit = {
    println("oid = " + oid, "name = " + name, "price = " + price)
  }
}


object AbstractClassDemo {
  def main(args: Array[String]): Unit = {
    val waitPayOrder = new WaitPayOrder
    println(waitPayOrder.oid)
    waitPayOrder.orderInfo
  }
}

5.5 trait介面

trait 類似於 Java 的 interface ,其用法與abstract class一致,只不過abstract class可以使用構造器而trait不可以, 用法如下:

trait Person{
  var name:String
  var age:Int

  def speak(): Unit ={
    println("speak...")
  }

  def eat
}

class Student extends Person{
  override var name: String = _
  override var age: Int = _

  override def eat: Unit = {
    print(s"$name is eating...")
  }
}

什麼時候應該使用trait而不是抽象類? 如果你想定義一個類似介面的型別, 你可能會在trait和抽象類之間難以取捨. 這兩種形式都可以讓你定義一個型別的一些行為, 並要求繼承者定義一些其他行為. 一些經驗法則:

  1. 優先使用trait. 一個類擴充套件多個trait是很方便的, 但卻只能擴充套件一個抽象類.

  2. 如果你需要建構函式引數, 使用抽象類. 因為抽象類可以定義帶引數的建構函式, 而trait不行.

5.6 伴生類和伴生物件

假如一個class與object同時修飾ObjectDemo,此時class ObjectDemo叫做object ObjectDemo的伴生類;而object ObjectDemo叫做class ObjectDemo的伴生物件,如下:

class ObjectDemo { }
object ObjectDemo{ }

object Test {

  def main(args: Array[String]): Unit = {
    val o1 = new ObjectDemo
    val o2 = ObjectDemo
    //(o1 = [email protected],o2 = [email protected])
    println("o1 = " + o1, "o2 = " + o2)
  }
}

伴生物件是一個單例物件:

object ObjectDemo {
  var element = 0

  def increase {
    element += 1
  }
}

//呼叫如下
for (i <- 1 to 10) {
    ObjectDemo.increase
}
println(ObjectDemo.element)    //10

通過伴生物件的apply方法實現物件的初始化(如果呼叫伴生物件(),則會間接apply()方法,可以在該方法中實現物件的初始化 ):

object ObjectDemo {
  def apply() = {
    new ObjectDemo
  }
  ...
}

//呼叫如下
val od1 = ObjectDemo()
val od2 = ObjectDemo()
//(od1 = [email protected],od2 = [email protected])
println("od1 = " + od1, "od2 = " + od2)