前言:
類和對象的相關知識比較多,筆者分爲兩篇來介紹,本篇即第一篇主要介紹類定義及對象創建、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);
}