在物件導向語言的世界中,類別可以說是核心。
有人形容類別像藍圖、設計圖,對某類特定事物的設計,針對這類事物定義的是包含哪些資料、可以做哪些事。定義完成後的class便可以將其實例化,也就是實現藍圖成為一個物件。
舉例來說,人、車、動物、建築、遊戲玩家...等都可以封裝在類別中做設計,人會有姓名、年紀等屬性,車子會有品牌、顏色等屬性,這些屬性通常會是名詞,並且每個類還可以有屬於該類別會有的行為,人會吃會睡、車子會跑會停...等,這些行為都可以用函數來定義,而函數名通常會用動詞。
打開IntelliJ專案結構,在其中的src/main/kotlin資料夾上點選滑鼠右鍵->New->kotlin class
然後New Kotlin Class視窗中選擇Class選項,並替 class 命名即可,採用大駝峰式命名,這裡我輸入Robot作為類別的名稱,就這樣建立完成囉,在kotlin資料夾可以看到這個檔案。
接續剛剛創建好新的class.kt檔後,打開該檔案應該就會有同名的class了,如下:
class Robot{ }
類別通常像這樣,位於一個同名的檔案中,但kotlin也允許兩者不同名。並可以在同一檔案中定義多個類別(如果是具有類似功能的類別的話)。
一個類別可以有一個主建構函數和多個次建構函數,每個類別一開始都是預設一個空的建構函數()
,上面Robot類別的建構函數()
因為沒有參數的主建構函數所以被省略。
但實例化時就算建構函數沒有參數也不能省略小括號喔。比如 : val robot = Robot()
我們呼叫建構函數時,它會負責將類別實例化,並且建構函數還允許我們傳入客製化初始值,以便實例化時指派給類別屬性當初始值。
繼續看看怎麼定義主建構函數,這將是我們最常使用的建構函數。
如果主構造函數沒有任何註解或可見性修飾符,可以省略這個constructor 關鍵字。主構造函數不能包含任何的程式碼。
主構造函數定義需要的參數_type
、_color
,便於呼叫時傳入引數。這兩個臨時變數也提供類別屬性type
和color
當初始值。
變數名稱前加底線「_」是為了便於辨識臨時變數及只參照一次的參數。
//不省略constructor
class Robot constructor(_type:String,_color:String){
val type =_type
val color = _color
}
//省略constructor
class Robot(_type:String,_color:String){
val type = _type
val color = _color
}
像type
和color
定義在類別中的變數就叫類別屬性,可以說是這個類別具有的特徵。而屬性一被定義,kotlin就會自動產生一個field、一個getter、一個setter:
Kotlin會提供預設的getter、setter,但也可以覆寫他們來達到客製化。
當類別都定義好了,我們就可以試著在同一個檔案中class Robot以外的地方呼叫main函數,並在其中建立Robot的實例,如下:
class Robot(...){...}
fun main() {
//實例化了名字叫robotCat的貓型機器人並且是藍色的。
val robotCat = Robot("cat","blue")
//使用「.」句點運算子來取得屬性值。
println("type : ${robotCat.type} , color : ${robotCat.color}")
//結果: type : cat , color : blue
}
除了兩個類別屬性外,我們的機器人類別應該也要設計一些功能吧。屬性跟函數沒有設置先後順序的問題,單獨只有兩者之一也ok。
class Robot(_type : String, _color : String) {
val type =_type
val color = _color
//加入work函數,work()被呼叫時簡單在控制台印出字串
fun work(){
println("機器人正執行您的指令")
}
}
在main函數中,使用「.」句點運算子呼叫work()看看 : 類別名.函數名()
fun main() {
val robotCat = Robot("cat","blue")
robotCat.work() //結果 : 機器人正執行您的指令
}
定義類別屬性這件事,kotlin提供了更簡便的方式,直接在主建構函數中定義,修改Robot類別試試:
class Robot(var type : String , var color : String) {
//刪除 val type =_type
//刪除 val color = _color
fun work(){
println("機器人正執行您的指令")
}
}
一切如此清爽,改好後再次運行main函數,應該一切完好如初。
一開始有說一個類別可以有多個次建構函數,如果已經定義主建構函數的情況下,代表著建立實例都必須透過它,次建構函數就是多了一種建立實例的方法(但仍須滿足主建構函數,意思是次建構函數必須直接或間接調用主建構函數)。
// 我多加了一個電池續航力的屬性batteryLife
class Robot(val type: String,
val color: String,
val batteryLife: Int) {
// 次建構函數:
constructor(type: String) : this(type, color = "yellow", batteryLife = 72)
fun work(){...}
}
次建構函數通過constructor關鍵字定義。
上面次建構函數中的this關鍵字指的是呼叫目前類別的主建構函數。
而這個次建構函數,實例化時指輸入type,其他的都會套用它已經設好的初始值 : 顏色是黃色、續航力72小時。
fun main() {
val robotCat = Robot("cat", "blue", 84)
val robotDog = Robot("dog")
println("robotCat :\ntype :${robotCat.type} , color: ${robotCat.color} , batteryLife : ${robotCat.batteryLife}hours")
println("robotDog :\ntype :${robotDog.type} , color: ${robotDog.color}, batteryLife : ${robotDog.batteryLife}hours\"")
//運行結果:
// robotCat :
// type :cat , color: blue , batteryLife : 84hours
// robotDog :
// type :dog , color: yellow, batteryLife : 72hours"
}
在類別中,以關鍵字init
定義一個初始化區塊,指該類別實例一被建立就會執行的區塊。初始化區塊可以設定變數或值,以及執行有效性檢查...等。
一旦初始化區塊有一項不通過便會拋出IllegalArgumentException異常。
例如我們可以要求機器人type必須設置、檢查電池有沒有損壞,如下:
class Robot(
val type: String,
val color: String,
val batteryLife: Int
) {
//次建構函數:
constructor(type: String) : this(type, color = "yellow", batteryLife = 72)
//初始化區塊 : 檢查type屬性有無設定、batteryLife 不可小於等於0、沒問題就會印出字串
init {
require(type.isNotBlank(), { "type屬性必須設定" })
require(batteryLife > 0, { "電池可能已損壞無法使用" })
println("Initialize a new Robot object : type :$type ,color : $color , battery life : $batteryLife hours")
}
fun work() {...}
}
參考: kotlin文檔、權威2.0、第一行代碼Android