【Kotlin】第6課-類別的設計(一)

在程式設計中,如果有些資料是相關的,例如課程名稱與學分數,可以將它們包裝成類別,這樣就能一併帶著走。而類別也可將實作細節封裝成方法,提供給外部使用,在主程式便能輕鬆地呼叫。本文將介紹如何設計基本的類別,並建立物件進行操作。

一、建立類別

我們說「類別是物件的設計圖」,因此首先要建立類別。在 IntelliJ,選擇想要的專案目錄後按下右鍵,點擊「New → Kotlin File/Class」。接著輸入名稱,選擇種類為類別(Class)。此處建立了名為「Course」(課程)的類別,按下 OK 就能建立。

除了類別(Class),在下拉式選單也可選擇「介面」(Interface)、「列舉」(Enum Class)等項目。


二、主要建構式與屬性

為了建立物件,需要有建構式,傳入的參數通常會作為這個物件的屬性。以下的範例是 Course 類別的主要建構式,它會寫在類別名稱的旁邊。

class Course constructor(val id: String,
var name: String,
var credit: Int) {
}

建構式參數也像方法一樣可以給予預設值。當所有參數都有預設值,相當於該類別具備無參數的建構式

Course 的建構式包含了 idnamecredit 參數。若用 var 與 val 語法宣告為變數或常數,意味著「參數即屬性」。如果不使用 var 或 val,則參數就只是參數,它不會成為物件的屬性。

屬性也可以自行在類別中宣告,並取用建構式參數來做賦值的動作。以下用新建立的 Student 類別(學生)來示範。

class Student constructor(stuId: String,
firstName: String,
lastName: String) {
val id = stuId
var fullName = "$firstName $lastName"
}

認識完主要建構式,接下來回到主程式的 main 方法,練習建立 Course 物件,並存取它的屬性。

// 建立物件,不需使用new語法
val course = Course("GN001", "English I", 2)

// 讀取欄位值
println("Id: ${course.id}")
println("Name: ${course.name}")
println("Credit: ${course.credit}")

// 賦予欄位值
course.name = "Basic English I"
println("Name: ${course.name}")

// course.id = "GN002" // 編譯錯誤:id屬性是常數,不可再賦值

存取的方式是「物件.屬性」。Kotlin 的類別不需自行宣告 get 與 set 方法,它們會隱含地被建立,因此上面的程式其實是在呼叫這些方法。而 Kotlin 屬性的存取權限預設為 public,get 與 set 方法預設也會與對應的屬性同步,所以上面的範例才能直接在主程式呼叫。

三、次要建構式

既然有主要建構式,那就有次要的。主要建構式的參數可以決定屬性值。觀察第二節的 CourseStudent,前者的參數是直接成為物件屬性,後者則是另外賦予給自行宣告的屬性。

次要建構式就像方法的「多載」(overloading),也就是接收不同的參數。然而還是要「回報」資料給其他建構式,輾轉傳回主要建構式,範例如下。

class Student constructor(stuId: String,
firstName: String,
lastName: String) {
val id = stuId
var fullName = "$firstName $lastName"
var gender = false

constructor(stuId: String, fullName: String, gender: Boolean)
: this(stuId, fullName, "") {
this.gender = gender
println("Sub constructor finished.")
}
}

次要建構式接收 stuIdfullNamegender 參數。這裡使用「: this()」語法回報,根據所指定的建構式的參數,來傳遞資料。接著將 gender 參數直接賦予給類別屬性。若建構式結束後,讀者想要執行額外的程式,可以寫在大括號中。沒有的話也能省略。


四、覆寫 get 與 set 方法

使用「物件.屬性」的語法,預設就只是單純地做取值和賦值。屬性值是什麼,就 get 什麼,指定值是什麼,就 set 什麼。

若想要有一些自己的實作,我們還是可以覆寫的。然而必須捨棄主要建構式中「參數即屬性」的用法。

以下範例是覆寫 StudentfullName 屬性的 get 方法。在宣告下方緊接著「get() {}」的語法就能覆寫。此處是當 fullName 為空字串時,取值要回傳「(Not provided)」的字串,否則回傳原屬性值。field 語法代表原資料的意思。

class Student constructor(stuId: String,
firstName: String,
lastName: String) {
val id = stuId

var fullName = "$firstName $lastName"
get() {
return if (field.isEmpty()) "(Not provided)" else field
}
}

以下範例是覆寫對 Coursecredit 屬性的 set 方法。在宣告下方緊接著「set(value) {}」的語法就能覆寫。此處是當傳入值小於零時,改為對 credit 賦予0,否則就採用原本傳入的參數。field 語法代表屬性值的意思,value 參數則是主程式的傳入值。

class Course constructor(val id: String,
var name: String,
_credit: Int) {
var credit = _credit
set(value) {
field = if (value < 0) 0 else value
}
}


五、宣告類別方法

類別的屬性有各自的 get 與 set 方法可使用,當然我們也可以在類別中宣告自己想要的方法。

class Student constructor(val id: String,
firstName: String,
lastName: String) {
var fullName = "$firstName $lastName"

fun getDetail() = "Id: $id, Full name: $fullName"
}

要注意的是方法只能存取到類別的屬性,不能取得建構式的參數。因此以上面的程式為例, getDetail 方法是不能拿到 firstNamelastName 的。

宣告與使用方法的方式其實和之前所學並沒有什麼不同。

val anna = Student("1056001", "Anna", "Su")

// 印出:Id: 1056001, Full name: Anna Su
println(anna.getDetail())

上一篇:【Kotlin】第5課-集合(List、Set、Map)

下一篇:【Kotlin】第7課-類別的設計(二)

留言