文章出處

一、主從構造器

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 Object People
main constructor in Class People
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家鬼",絕對的忠貞不二。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()