1. 程式人生 > >Scala入門到精通——第七節:類和物件(二)

Scala入門到精通——第七節:類和物件(二)

本節主要內容

  1. 單例物件
  2. 伴生物件與伴生類
  3. apply方法
  4. 應用程式物件
  5. 抽象類

單例物件

在某些應用場景下,我們可能不需要建立物件,而是想直接呼叫方法,但是Scala語言並不支援靜態成員,Scala通過單例物件來解決該問題。單例物件的建立方式如下:

object Student {
  private var studentNo:Int=0;
  def uniqueStudentNo()={
    studentNo+=1
    studentNo
  }
  def main(args: Array[String]): Unit = {
    println(Student.uniqueStudentNo())
  }
}

object Student編碼後將生成兩個位元組碼檔案
這裡寫圖片描述

利用javap命令檢視位元組碼檔案內容有:

D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Student$
警告: 二進位制檔案Student$包含cn.scala.xtwy.Student$
Compiled from "Student.scala"
public final class cn.scala.xtwy.Student$ {
  public static final cn.scala.xtwy.Student$ MODULE$;
  private
int studentNo; public static {}; private int studentNo(); private void studentNo_$eq(int); public int uniqueStudentNo(); private cn.scala.xtwy.Student$(); } D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Student 警告: 二進位制檔案Student包含cn.scala.xtwy.Student Compiled from
"Student.scala" public final class cn.scala.xtwy.Student { public static void main(java.lang.String[]); public static int uniqueStudentNo(); }

不難看出,object Student最終生成了兩個類,分別是Student與StudentfinalStudent的構造方法是私有的,通過靜態成員域 public static final cn.scala.xtwy.Student$ MODULE$; 對Student$進行引用,這其實是java語言中單例實現方式。
單例物件的使用方式同Java語言類引用靜態成員是一樣的。

伴生物件與伴生類

在前面單例物件的基礎之上,我們在object Student所在的檔案內定義了一個class Student,此時object Student被稱為class Student的伴生物件,而class Student被稱為object Student的伴生類:

class Student(var name:String,age:Int)

object Student {
  private var studentNo:Int=0;
  def uniqueStudentNo()={
    studentNo+=1
    studentNo
  }
  def main(args: Array[String]): Unit = {
    println(Student.uniqueStudentNo())
  }
}

//生成的位元組碼檔案如下:
D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Student
警告: 二進位制檔案Student包含cn.scala.xtwy.Student
Compiled from "Student.scala"
public class cn.scala.xtwy.Student {
  private java.lang.String name;
  private int age;
  public static void main(java.lang.String[]);
  public static int uniqueStudentNo();
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public cn.scala.xtwy.Student(java.lang.String, int);
}

D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Student$
警告: 二進位制檔案Student$包含cn.scala.xtwy.Student$
Compiled from "Student.scala"
public final class cn.scala.xtwy.Student$ {
  public static final cn.scala.xtwy.Student$ MODULE$;
  private int studentNo;
  public static {};
  private int studentNo();
  private void studentNo_$eq(int);
  public int uniqueStudentNo();
  public void main(java.lang.String[]);
  private cn.scala.xtwy.Student$();
}

從上面的程式碼中不難看出,其實伴生物件與伴生類本質上是不同的兩個類,只不過伴生類與伴生物件之間可以相互訪問到對主的成員包括私有的成員變數或方法,例如:

class Student(var name:String,var age:Int){
  private var sex:Int=0
  //直接訪問伴生物件的私有成員
  def printCompanionObject()=println(Student.studentNo)
}

object Student {
  private var studentNo:Int=0;
  def uniqueStudentNo()={
    studentNo+=1
    studentNo
  }
  def main(args: Array[String]): Unit = {
    println(Student.uniqueStudentNo())
    val s=new Student("john",29)
    //直接訪問伴生類Student中的私有成員
    println(s.sex)
  }
}

apply方法

在前幾節中我們提到,通過利用apply方法可以直接利用類名建立物件,例如前面在講集合的時候,可以通過val intList=List(1,2,3)這種方式建立初始化一個列表物件,其實它相當於呼叫val intList=List.apply(1,2,3),只不過val intList=List(1,2,3)這種建立方式更簡潔一點,但我們必須明確的是這種建立方式仍然避免不了new,它後面的實現機制仍然是new的方式,只不過我們自己在使用的時候可以省去new的操作。下面就讓我們來自己實現apply方法,程式碼如下:

//定義Student類,該類稱為伴生類,因為在同一個原始檔裡面,我們還定義了object Student
class Student(var name:String,var age:Int){
  private var sex:Int=0
  //直接訪問伴生物件的私有成員
  def printCompanionObject()=println(Student.studentNo)

}

//伴生物件
object Student {
  private var studentNo:Int=0;
  def uniqueStudentNo()={
    studentNo+=1
    studentNo
  }
  //定義自己的apply方法
  def apply(name:String,age:Int)=new Student(name,age)
  def main(args: Array[String]): Unit = {
    println(Student.uniqueStudentNo())
    val s=new Student("john",29)
    //直接訪問伴生類Student中的私有成員
    println(s.sex)

    //直接利用類名進行物件的建立,這種方式實際上是呼叫前面的apply方法進行實現,這種方式的好處是避免了自己手動new去建立物件
    val s1=Student("john",29)
    println(s1.name)
    println(s1.age)
  }
}

應用程式物件

利用IDE開發scala應用程式時,在執行程式時必須指定main方法作為程式的入口,例如:

object Student {
  //必須定義mian方法作為程式的入口才能執行
  def main(args: Array[String]): Unit = {
    val s1=Student("john",29)
    println(s1.name)
    println(s1.age)
  }
}

除了這種方式之外,scala還提供了一種機制,即通過擴充套件App,在Scala IDE for Eclipse裡是通過new->scala app方式建立的
這裡寫圖片描述

也可在程式碼直接指定:

//擴充套件App後,程式可以直接執行,而不需要自己定義main方法,程式碼更簡潔
object AppDemo extends App {
  println("App Demo")
}

App其實是一種trait,它幫助我們定義了main方法。

抽象類

抽象類是一種不能被例項化的類,抽象類中包括了若干不能完整定義的方法,這些方法由子類去擴充套件定義自己的實現。

//scala中的抽象類定義
abstract class Animal {
  def eat:Unit
}

//對應位元組碼檔案
D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Animal.class

Compiled from "human.scala"
public abstract class cn.scala.xtwy.Animal {
  public abstract void eat();
  public cn.scala.xtwy.Animal();
}

除抽象方法外,抽象類中還可以有抽象欄位:

abstract class Animal {
  //抽象欄位(域)
  //前面我們提到,一般類中定義欄位的話必須初始化,而抽象類中則沒有這要求
  var height:Int
  //抽象方法
  def eat:Unit
}

//Person繼承Animal,對eat方法進行了實現
//通過主構造器對height引數進行了初始化
class Person(var height:Int) extends Animal{
  //對父類中的方法進行實現,注意這裡面可以不加override關鍵字
  def eat()={
    println("eat by mouth")
  }

}

//通過擴充套件App建立程式的入口
object Person extends App{
  new Person(10).eat()
}
//上面這幾個類會產生以下四個位元組碼檔案
 D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy 的目錄

2015/07/22  23:28    <DIR>          .
2015/07/22  23:28    <DIR>          ..
2015/07/22  23:28               675 Animal.class
2015/07/22  23:28             2,143 Person$.class
2015/07/22  23:28               699 Person$delayedInit$body.class
2015/07/22  23:28             1,741 Person.class


//位元組碼內容如下:
//Animal類對應的位元組碼,可以看到,位元組碼中包括了抽象欄位height的getter和setter方法,只不過它們都是抽象的
D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Animal.class

Compiled from "Person.scala"
public abstract class cn.scala.xtwy.Animal {
  public abstract int height();
  public abstract void height_$eq(int);
  public abstract void eat();
  public cn.scala.xtwy.Animal();
}

//Person類對應的位元組碼檔案
D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Person.class

Compiled from "Person.scala"
public class cn.scala.xtwy.Person extends cn.scala.xtwy.Animal {
  private int height;
  public static void main(java.lang.String[]);
  public static void delayedInit(scala.Function0<scala.runtime.BoxedUnit>);
  public static java.lang.String[] args();
  public static void scala$App$_setter_$executionStart_$eq(long);
  public static long executionStart();
  public int height();
  public void height_$eq(int);
  public void eat();
  public cn.scala.xtwy.Person(int);
}

//伴生物件Person對應的位元組碼檔案內容
D:\ScalaWorkspace\ScalaChapter06_2\bin\cn\scala\xtwy>javap -private Person$.clas
s
Compiled from "Person.scala"
public final class cn.scala.xtwy.Person$ implements scala.App {
  public static final cn.scala.xtwy.Person$ MODULE$;
  private final long executionStart;
  private java.lang.String[] scala$App$$_args;
  private final scala.collection.mutable.ListBuffer<scala.Function0<scala.runtim
e.BoxedUnit>> scala$App$$initCode;
  public static {};
  public long executionStart();
  public java.lang.String[] scala$App$$_args();
  public void scala$App$$_args_$eq(java.lang.String[]);
  public scala.collection.mutable.ListBuffer<scala.Function0<scala.runtime.Boxed
Unit>> scala$App$$initCode();
  public void scala$App$_setter_$executionStart_$eq(long);
  public void scala$App$_setter_$scala$App$$initCode_$eq(scala.collection.mutabl
e.ListBuffer);
  public java.lang.String[] args();
  public void delayedInit(scala.Function0<scala.runtime.BoxedUnit>);
  public void main(java.lang.String[]);
  private cn.scala.xtwy.Person$();
}

新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊
這裡寫圖片描述