Scala 編程——第六節:類和對象(一)

前言:
       類和對象的相關知識比較多,筆者分爲兩篇來介紹,本篇即第一篇主要介紹類定義及對象創建、getter/setter、類主構造器、輔助構造器。

1.類定義及創建對象

1.1 類

定義類

// 採用關鍵字class定義
class Person {
  // 類成員必須初始化,否則會報錯
  // 這裏定義的是一個公有成員
  var name:String=null
}

Person類編譯後會生成Person.class文件,使用 javap -p Person 命令反編譯代碼

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String name;
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public com.harvey.classandobject.Person();
}

從字節碼文件內容可以看到:雖然我們只在Person類中定義了一個類成員(域)name,類型爲String,但Scala會默認幫我們生成name()與name_=()及構造函數Person()。其中name()對應java中的getter方法,name_=()對應java中的setter方法(由於JVM中不允許出現=,所以用$eq代替。值得注意的是定義的是公有成員,但生成的字節碼中卻是以私有的方式實現的,生成的getter、setter方法是公有的。

Scala中定義的類同Java一樣,有一個默認的構造方法。因此,可以直接new操作創建Person對象。

val person = new Person()
person.name_=("tom") // setter 方法
println(person.name) // getter 方法
person.name = "jonh" // 直接修改,其實調用的是person.name_=("jonh")
println(person.name) // getter 方法

1.2 getter setter

Scala 中可以定義自己的getter和setter方法,修改Person類代碼如下

class Person {
  private var privateName: String = null // 定義私有成員

  def name = this.privateName // getter 方法

  def name_=(name: String): Unit = { // setter 方法
    this.privateName = name
  }
}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String privateName;
  private java.lang.String privateName();
  private void privateName_$eq(java.lang.String);
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public com.harvey.classandobject.Person();
}

上面我們定義了一個私有變量(private修飾),Scala會默認幫我們生成getter和setter方法,但是他們也是私有的,能直接使用的是我們自定義的getter和setter方法,如下

val person = new Person()
println(person.name) // null
person.name = "harvey"
println(person.name) // harvey

我們知道Scala中變量的修飾有兩種,上面用的都是var,如果使用val修飾變量,scala則只會生成getter方法,如下

class Person {
  val name:String=null
}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private final java.lang.String name;
  public java.lang.String name();
  public com.harvey.classandobject.Person();
}

如果將成員域定義爲private[this],那麼這個字段是對象私有的,這種情況下,不會生成getter和setter。對象私有字段,只能由當前對象的方法訪問,而該類的其他對象的方法是無法訪問的

class Person {
  private[this] val name:String=null
}

字節碼文件:

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  public com.harvey.classandobject.Person();
}

Scala中可以使用private[class-name]來指定可以訪問該字段的類,class-name必須是當前定義的類,或者是當前定義的類的外部類。這種情況會生成getter和setter方法。

1.3 Bean 屬性

Java中我們定義的JavaBean生成的都是getXXX()、setXXX()方法,Scala中生成的getter()和setter()不是這樣的(上述示例中可以看出),如果也想和Java中一樣,需要引入BeanProperty,然後使用採用註解的方式修飾變量。使用該註解後,將會生成4個方法:Scala的getter/setter和JavaBeans規範的getter/setter(如果是val聲明,就沒有setter部分了)

// 在Scala 2.10.0之後已被廢棄
// import scala.reflect.BeanProperty
import scala.beans.BeanProperty
class Person {
  @BeanProperty var name:String = "john"
}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String name;
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public void setName(java.lang.String);
  public java.lang.String getName();
  public com.harvey.classandobject.Person();
}

2.類主構造器

2.1 主構造器定義

2.1.1 有參主構造器

主構造器的定義與類的定義交織在一直,將構造器參數直接放在類名稱之後,如下代碼

// 下列代碼定義了類Person,還定義了參數爲String、Int類型的主構造器
class Person (val name: String, val age: Int)

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private final java.lang.String name;
  private final int age;
  public java.lang.String name();
  public int age();
  public com.harvey.classandobject.Person(java.lang.String, int);
}

上述代碼使用Java語言編寫如下

public class Person{
  private final String name;
  private final int age;
  public Person(String name,int age){
       this.name=name;
       this.age=age;
  }
  public String getName(){ return name}
  public int getAge() {return age}
}

具體操作

object Test {
  def main(args: Array[String]): Unit = {
    val person = new Person("tom", 18)
    println("name=" + person.name + ",age=" + person.age) // 運行結果:name=tom,age=18
  }
}

2.1.2 無參主構造器

Scala中主構造器的定義可以有參數,也可以無參數,如下代碼

// 下列代碼定義了類Person,無參主構造器
class Person

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  public com.harvey.classandobject.Person();
}

具體操作

object Test {
  def main(args: Array[String]): Unit = {
    val person = new Person() // 創建Person的實例
  }
}

2.1.3 使用主構造器進行初始化操作

主構造器會執行類中定義的所有語句,如在創建對象時,需要進行相關初始化操作時,可以將初始化語句放在類體中,同樣也可以在類中添加或重寫相關方法

class Person(val name: String, val age: Int) {
  println("init opertion...")

  def show(): Unit = {
    println("Hello Scala")
  }

  // 重寫toString()方法
  override def toString() = "name = " + name + ",age = " + age
}

具體操作

object Test {
  def main(args: Array[String]): Unit = {
    val person = new Person("tom", 18)
    person.show()
    println(person.toString())
  }
}

運行結果

init opertion...
Hello Scala
name = tom,age = 18

2.2 主構造器參數

上面我們定義了有參主構造器,主構造器參數可以有默認值,如下代碼

class Person(val name: String = "", val age: Int = 18) {

}

具體操作

object Test {
  def main(args: Array[String]): Unit = {
    // 使用主構造參數的默認值
    val person1 = new Person
    println("name = " + person1.name + ",age = " + person1.age)

    // 指定參數值
    val person2 = new Person("john", 20)
    println("name = " + person2.name + ",age = " + person2.age)
  }
}

2.3 主構造器參數訪問控制

主構造器中的參數是可以加訪問控制符的,我們先來看下不加參數情況

class Person(name: String, age: Int) {
  override def toString()= "name = " + name + ",age = " + age
}

等同於如下代碼

class Person(private[this] val name: String,private[this] val age: Int) {
  override def toString()= "name = " + name + ",age = " + age
}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private final java.lang.String name;
  private final int age;
  public java.lang.String toString();
  public com.harvey.classandobject.Person(java.lang.String, int);
}

默認參數的主構建器,參數帶訪問控制符號
從字節碼文件中可以看到,當主構造器的參數不用var或val修飾的時候,參數會生成類的私有val成員,並且不會產生getter和setter方法

需要注意的是,將上述Person類中的toString()方法去掉,則類中無任何地方使用了主構造器的參數,此時主構造器參數不會生成類成員,修改代碼如下

class Person(name: String, age: Int) {

}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  public com.harvey.classandobject.Person(java.lang.String, int);
}

2.4 禁用主構造器

在某些情況下,可能需要禁用主構建器,代碼如下

class Person private(var name: String, var age: Int) {

}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String name;
  private int age;
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  private com.harvey.classandobject.Person(java.lang.String, int);
}

3.輔助構造器

前面講了,如果禁用掉了主構建器,則必須使用輔助構造函數來創建對象。輔助構造函數具有兩個特點:
(1). 輔助構建器的名稱爲this,java中的輔助構造函數與類名相同,這常常會導致修改類名時出現不少問題,scala語言避免了這樣的問題
(2). 調用輔助構造函數時,必須先調用主構造函數或其它已經定義好的構造函數。

3.1 類中只有輔助構造函數

如下Person類中只有輔助構造函數

class Person {
  // 類成員,私有
  private var name: String = null;
  private var age: Int = 18;
  private var gender: Int = 0;

  // 輔助構造器
  def this(name: String) {
    this()
    this.name = name
  }

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

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

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String name;
  private int age;
  private int gender;
  private java.lang.String name();
  private void name_$eq(java.lang.String);
  private int age();
  private void age_$eq(int);
  private int gender();
  private void gender_$eq(int);
  public com.harvey.classandobject.Person();
  public com.harvey.classandobject.Person(java.lang.String);
  public com.harvey.classandobject.Person(java.lang.String, int);
  public com.harvey.classandobject.Person(java.lang.String, int, int);
}

在定義輔助構造函數時,需要注意構造函數順序,如下代碼

class Person{
  // 類成員,私有
  private var name:String=null
  private var age:Int=18
  private var sex:Int=0

 // 輔助構造器
 def this(name:String,age:Int,sex:Int){
    this(name,age) // 此處會發生編譯錯誤,這是因爲def this(name:String,age:Int)沒有被定義
    this.sex=sex
  }

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

3.2 類中既有主構造器也有輔助構造函數

如下定義的Person類中,既有主構造器也有輔助構造函數

// 主構造器
class Person(var name: String, var age: Int) {

  // 類成員,私有
  private var gender: Int = 0

  // 輔助構造器
  def this(name: String, age: Int, gender: Int) {
    this(name, age)
    this.gender = gender
  }
}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String name;
  private int age;
  private int gender;
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  private int gender();
  private void gender_$eq(int);
  public com.harvey.classandobject.Person(java.lang.String, int);
  public com.harvey.classandobject.Person(java.lang.String, int, int);
}

3.3 禁用主構造器,使用輔助構造函數創建對象

上述主構造器中我們有提到,主構造器是有可能被禁用的,此時是能通過輔助構造函數來創建對象

// 禁用主構造器
class Person private(var name: String, var age: Int) {

  // 類成員,私有
  private var gender: Int = 0

  // 輔助構造器
  def this(name: String, age: Int, gender: Int) {
    this(name, age)
    this.gender = gender
  }
}

字節碼文件

D:\code\study\scalademo\target\classes\com\harvey\classandobject>javap -p Person
警告: 二進制文件Person包含com.harvey.classandobject.Person
Compiled from "Person.scala"
public class com.harvey.classandobject.Person {
  private java.lang.String name;
  private int age;
  private int gender;
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  private int gender();
  private void gender_$eq(int);
  private com.harvey.classandobject.Person(java.lang.String, int);
  public com.harvey.classandobject.Person(java.lang.String, int, int);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章