天天看點

scala 學習筆記(04) OOP(上)主從構造器/私有屬性/伴生對象(單例靜态類)/apply方法/嵌套類

scala 學習筆記(04) OOP(上)主從構造器/私有屬性/伴生對象(單例靜态類)/apply方法/嵌套類

一、主從構造器

java中構造函數沒有主、從之分,隻有構造器重載,但在scala中,每個類都有一個主構造器,在定義class時,如果啥也沒寫,預設有一個xxx()的主構造器

class Person {

  var name: String = _

  /**
   * 從構造器
   * @param name
   */
  def this(name: String) = {
    this //注意:從構造器,必須先調用主構造器
    this.name = name;
  }

  override def toString = {
    "name:" + name
  }

}
      

上面的這段代碼,如果将生成的class反編譯成java來看的話,可能更容易了解:

public class Person
{
  private String name;

  public String name()
  {
    return this.name; } 
  public void name_$eq(String x$1) { this.name = x$1; }


  public String toString()
  {
    return new StringBuilder().append("name:").append(name()).toString();
  }

  public Person()
  {
  }

  public Person(String name)
  {
    this();
    name_$eq(name);
  }
}
      

從反編譯結果看,生成了二個構造函數,一個是預設的Person(),另一個是Person(String name)。

Scala是一個崇尚簡約之美的語言,在定義Class時,可以将屬性聲明、帶參構造器,一并全解決了,是以剛才這段代碼,"等效"于下面這樣:

class Person(var name: String) {

  override def toString = {
    "name:" + name
  }

}
      

 注意第一行,此時的主構造器不再是預設的無參構造器,而是Person(var name:String),它有二層意思:一是定義了一個帶參數的構造器,二是由于name前加了var,說明name:String不僅僅是構造器的參數,還是類Person的一個屬性成員,不過這個版本與第一個版本還是有些差别的,如果用JD-GUI反編譯檢視的話,會發現預設的無參構造器消失了

public class Person
{
  private String name;

  public String name()
  {
    return this.name; } 
  public void name_$eq(String x$1) { this.name = x$1; }

  public String toString() {
    return new StringBuilder().append("name:").append(name()).toString();
  }

  public Person(String name)
  {
  }
}
      

 Person的使用示例如下:

object App {

  def main(args: Array[String]) {
    val p: Person = new Person("jimmy")
    println(p.toString)
    p.name = "jimmy.yang"
    println(p.toString)
  }

}
      

主構造器上,還可以增加限定詞private來修飾,比如下面這樣:

class Person private(var name: String) {

  var age: Int = 0;

  def this(age: Int, name: String) = {
    this(name)
    this.age = age;
  }

  override def toString = {
    "name:" + name + ",age:" + age
  }

}
      

這樣Person類的使用方,就隻能使用從屬構造器this(age:Int,name:String)了。

二、私有屬性(private property)

将前面的Person改一下,将年齡Age設定成私有成員

package yjmyzz


class Person private(var name: String) {

  println("這是主構造器的可執行語句,我是" + name) //這一行在new Person時會執行

  /**
   * 定義一個私有成員
   */
  private var _age: Int = 0;

  def age = _age


  def this(age: Int, name: String) = {
    this(name)
    this._age = age;
  }

  def isOlder(another: Person) = {
    this._age > another._age //注意:這裡可直接通路另一個Person的私有成員_age
  }

  override def toString = {
    "name:" + name + ",age:" + age
  }

}
      

 注意:isOlder方法,該方法用于比較二個Person誰更年長,跟java不同的是,在Class定義範圍内,可以直接通路另一個類執行個體的私有成員!這在java、c#中是絕對不允許的。

另外,還有一個值得注意的地方,Class的定義裡,除了def定義的方法(或許稱為函數更适合)外,任何可執行語句都會被執行,比如第6行的println語句。下面是調用示例:

val jimmy: Person = new Person(18, "jimmy")
    val mike: Person = new Person(30, "mike")
    if (mike.isOlder(jimmy))
      println(mike.name + " is older than " + jimmy.name)
      

執行結果:

這是主構造器的可執行語句,我是jimmy

這是主構造器的可執行語句,我是mike

mike is older than jimmy

如果不希望private成員在Class定義中直接被其它執行個體所通路,可以改成private[this],即:

private[this] var _age: Int = 0;

  def age = _age

  def isOlder(another: Person) = {
    this._age > another.age
  }
      

這樣的話,isOlder中的another,隻能通過函數age來通路私有成員_age了。

三、static成員/伴生對象Object/apply方法

scala裡并沒有static關鍵字,要達到類似的效果,可以借助object對象,object天然是singleton模式,比如下面的代碼:

object Singleton {

  var count = 0;

  def increment: Unit = {
    count += 1
  }

}
      

定義成object類型的對象,沒有辦法直接new, object中的所有方法都是靜态方法,這一點類似c#中的static靜态類,使用時直接按靜态方法調用即可:

var obj1 = Singleton.count
    println(obj1)
    Singleton.increment
    var obj2 = Singleton.count
    println(obj2)
      

object不僅僅用于單例模式的實作,更多時候,我們可以定義一個與class同名的object,然後把class的所有靜态方法放到object裡,比如:

class People(var name: String) {

  println("main constructor in Class People")

}


object People {

  def whoami: Unit = {
    println("I am a people ")
  }

  def apply(name: String) = {
    println("apply in Object People")
    new People(name)
  }

}
      

後面的object People稱為class People的伴生對象,可以了解為class People的靜态成員/方法集合,注意裡面的apply方法,這個方法會被自動調用,通常用于建立對象執行個體,有點工廠模式的意味,看下面的調用代碼:

var p:People = People("jimmy")
    println(p.name)
    People.whoami
      

這裡我們沒有用new關鍵字來建立對象,而是"隐式"調用了伴生對象的靜态方式apply,以下是輸出結果:

apply in Object People

main constructor in Class People

jimmy

I am a people

伴生對象+apply方法,這是scala中經常使用的一個技巧,即簡化了代碼,又起了工廠模式的作用,我們甚至還可以在apply方法中加入對象控制的額外業務邏輯,這比直接new對象更靈活。

從object的使用上,還可以看出靜态方法的調用上scala與java的不同,java中靜态方法即可以用"類名.靜态方法()",也可以用"對象執行個體.靜态方法()"來調用,說實話,有點不太講究,而Scala"糾正"了這一錯誤,靜态方法隻能在object(即:靜态類)上調用,非靜态方法隻能在對象執行個體上調用,這與c#的理念是一緻的(見:java學習:OOP入門 第7點)

apply方法不僅可以存在于object中,class中也可以有apply方法,我們把People的Class改一下:

class People(var name: String) {

  println("main constructor in Class People")

  def apply(): Unit ={
    println("apply in Class People")
  }

}
      

然後這麼調用:

var p:People = People("jimmy")
    p()
      

注意第2行,就是這麼簡單!輸出結果:

apply in Class People

四、内部類(也稱嵌套類)

class内部還可以再定義類,即嵌套類,與java不同的是,scala的嵌套類是屬于執行個體的,而不屬于定義它的外部類。這句話聽着很繞,還是直接看代碼吧,先把前面的People類改造一下:

class People(var name: String) {

  /**
   * 定義嵌套類(注:必須寫在最開始,好象也隻能定義一個?)
   */
  master =>

  //這個master變量即指People本身this,名字可以随便取

  class Pet(var name: String) {
    def hi() = println("我叫" + this.name + " , 我是" + master.name + "的寵物!")
  }


  /**
   * 增加了一個寵物屬性
   */
  var pet: Pet = _

}


object People {

  def apply(name: String, petName: String) = {
    println("apply in Object People")
    val people = new People(name)
    people.pet = new people.Pet(petName)
    people
  }

}
      

 然後使用:

val jimmy = new People("jimmy")
    val dog = new jimmy.Pet("wang wang") //注:這是調用的"執行個體"上的Pet,而不是new People.Pet()
    dog.hi()

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

    val mike = People("Mike","miao miao")
    mike.pet.hi()
    println("------------")


    //println("jimmy與mike交換寵物:")
    //jimmy.pet = mike.pet //直接報錯,因為mike的寵物是隻屬于mike的,它與jimmy的寵物類型不相容

    //jimmy又養了一隻貓
    var cat = new jimmy.Pet("miao")
    //然後把狗狗扔了
    jimmy.pet = cat;
    jimmy.pet.hi()
      

注意第2行及第13行,第2行是直接用 new 執行個體.内部類()的方式建立的,而非 new 外部類.内部類()這種方式,說明内部類是從屬于外部類的執行個體,第13行再次證明了這一點,雖然都是内部類Pet的執行個體,但當試圖将mike的Pet執行個體指派給jimmy的Pet執行個體時,編譯器直接報錯,說明内部類的執行個體一旦建立,則"生是X家人,死是X家鬼",絕對的忠貞不二。

作者:菩提樹下的楊過

出處:http://yjmyzz.cnblogs.com

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。