類
kotlin 中使用關鍵字 class 聲明類
類聲明由類名,類頭(指定其類型參數,主構造函數等),花括號包圍的類體構成,類頭 和類體都是可選的。
class Student {
}
如果一個類沒有類體,則可以省略花括號
class Empty
主構造函數
一個類可以有一個主構造函數和一個或多個次構造函數,主構造函數是類頭的一部分,主構造函數跟在 類名後。
class 類名 constructor(形參1,形參2,形參3){
}
class Employee constructor(firstName: String, lastName: String, age: Int) {
private var name: String
private var firstName: String
private var lastName: String
private var workTime: Int
init {
println("first init")
this.firstName = firstName // 主構造函數不能包含任何初始化代碼,所以放在 init 中初始化成員變量 firstName,lastName
this.lastName = lastName
this.name = this.firstName + this.lastName // 成員變量可以在 init 初始化代碼塊中初始化
workTime = age / 2
}
init {
println("second init")
}
}
// 輸出結果:
first init
second init
注意點:
- 關鍵字 constructor ,在 Java 中,構造方法名必須和類名相同,而在 kotlin 中,是通過 contructor 關鍵字來標明的,且對於主構造函數而言,它的位置是在 類頭(即緊跟類名的後面),而不是在類體中。
- 關鍵字 init : init 被稱作是初始化代碼塊,他的作用是爲主構造函數服務的,由於主構造函數是放在 類頭中的,是不能包含任何初始化語句的(這是語法規定的),這個時候就是 init 的用武之地了,我們可以把初始化執行語句放在 init 代碼塊中進行初始化,爲類的成員屬性賦值。
- 在實例初始化期間,初始化代碼塊按照他們出現在類體重的順序執行,與屬性初始化器交織在一起。
如果主構造函數沒有任何註解或可見性修飾符,則可以省略這個 constructor 關鍵字。
class Employee(fristName: String, lastName: String, age: Int) { // 省略 constructor 關鍵字
}
類的成員屬性初始化不是必須放在 init 代碼塊中的,可以在定義屬性時直接將主構造函數中的形參賦值給他。
class Employee constructor(firstName: String, lastName: String, age: Int) {
private var name: String = firstName + lastName
private var firstName: String = firstName
private var lastName: String = lastName
private var workTime: Int = age / 2
init {
println("first init")
this.firstName = firstName // 主構造函數不能包含任何初始化代碼,所以放在 init 中初始化成員變量 firstName,lastName
this.lastName = lastName
this.name = this.firstName + this.lastName // 成員變量可以在 init 初始化代碼塊中初始化
workTime = age / 2
}
init {
println("second init")
}
}
可以發現,這種在構造函數中聲明形參,然後在屬性定義進行賦值,有點繁瑣,我們可以直接在主構造函數中定義類的屬性。
class Computer(private var name: String, private var price: Int) {
fun printInfo() {
println("name = $name , price = $price")
}
}
fun main(args: Array<String>) {
var appleComputer = Computer("apple", 10000)
appleComputer.printInfo()
}
輸出結果:
name = apple , price = 10000
當我們在定義一個類時,如果沒有顯式的提供主構造函數,Kotlin 編譯器會默認生成一個無參主構造函數,這點和 Java 是一樣的
class Mouse {
private var name: String = "LenovoMousc"
private var price: Int = 23
fun printInfo() {
println("name = $name , price = $price")
}
}
fun main(args: Array<String>) {
var lenovoMouse = Mouse()
lenovoMouse.printInfo()
}
輸出結果:
name = LenovoMousc , price = 23
次構造函數
與主構造函數不同的是: 次構造函數是定義在類體中的,並且次構造函數可以有多個,而主構造函數只能有一個。
class MyButton : AppCompatButton {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.attr.buttonStyle)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}
注意點:
- 可以使用 this 關鍵字來調用 自己的其他構造函數
- 使用 super 關鍵字來調用父類構造函數
我們看下,同時定義主構造函數和多個次構造函數
class Mouse(name: String, price: Int, address: String) {
private var name: String = name
private var price: Int = price
private var address: String = address
constructor(name: String) : this(name, 0)
constructor(name: String, price: Int) : this(name, price, "")
fun printInfo() {
println("name = $name , price = $price, address = $address")
}
}
fun main(args: Array<String>) {
var lenovoMouse = Mouse("applemouse")
lenovoMouse.printInfo()
}
一個 三個參數的主構造函數,兩個次構造函數。一個參數的次構造函數調用了兩個參數的次構造函數,兩個參數的次構造函數調用了三個參數的主構造函數。
我們把 兩個參數的次構造函數的 this(name,price,"") 刪除掉,會出現什麼問題呢?
class Mouse(name: String, price: Int, address: String) {
private var name: String = name
private var price: Int = price
private var address: String = address
constructor(name: String) : this(name, 0)
constructor(name: String, price: Int) {} // 刪除掉調用三個參數的主構造器,會提示: Primary constructor call expected
fun printInfo() {
println("name = $name , price = $price, address = $address")
}
}
fun main(args: Array<String>) {
var lenovoMouse = Mouse("applemouse")
lenovoMouse.printInfo()
}
刪除三個參數的主構造函數會提示: Primary constructor call expected
結論:次構造函數會直接或間接調用主構造函數的
創建類的實例
要創建一個類的實例,可以普通函數一樣調用 構造函數。
kotlin 中沒有 new 關鍵字
var employee = Employee("1001", "xiaohe", 12)
類成員
類可以包含:
- 構造函數與初始化代碼塊
- 函數
- 屬性
- 嵌套類和內部類
- 對象聲明
繼承
在 kotlin 中所有類都有一個共同的超類 Any , 這對於沒有聲明超類型的類提供了默認的超類
注意點:
-
Kotlin 中如果類前面不加關鍵字 open, 則該類默認是 final 的 ,也就是不能被繼承,如果繼承該類,會報錯: This type is final, so it cannot be inherited from.
-
Any 類 默認提供了三個方法 : equal() , hashCode() ,toString()
-
基類中沒有定義構造器時,Kotlin 會默認添加一個無參的構造器,所以子類在繼承該基類時,需要實現該基類的無參構造器。
// 編譯器會提供的默認無參的構造器 open class Phone { } // 繼承基類時,需要實現基類中的無參的構造器 class ApplePhone(private var rom: Int) : Phone() { fun printInfo() { println("the rom is $rom") } }
輸出結果:
the rom is 4
-
在寫子類是,如果子類中有主構造函數,那麼基類必須要在主構造函數中立即初始化
// open 標識該類可以被繼承 open class Mouse(name: String, price: Int, address: String) { private var name: String = name private var price: Int = price private var address: String = address constructor(name: String) : this(name, 0) constructor(name: String, price: Int) : this(name, price, "") fun printInfo() { println("name = $name , price = $price, address = $address") } } class AppleMouse(name: String, price: Int, address: String, wireless: Boolean) : Mouse(name, price, address) { }
-
如果子類中沒有主構造函數,那麼必須要在此構造函數中調用 super 關鍵字初始化父類,或者調用另一個此構造函數。初始化父類時,可以調用父類的不同的構造函數。
重寫
重寫方法
在繼承基類時,子類可以重寫基類中的有 open 關鍵字修飾的方法,如果沒有 open 修飾的方法,是允許子類重寫的,重寫方法時,需要使用 override 關鍵字標識。
在 IDEA IDE 中,在子類中可以使用快捷鍵 Ctrl + O 快速重寫父類中被 open 關鍵字修飾的方法
// 基類有默認的無參構造器,所以子類繼承時,
open class Phone {
open fun getColor() {
println("phone has many colors")
}
}
class ApplePhone(private var rom: Int) : Phone() {
override fun getColor() {
super.getColor()
println("iPone color is red")
}
fun printInfo() {
println("the rom is $rom")
}
}
fun main(args:Array<String>) {
var iPhone = ApplePhone(4)
iPhone.getColor()
}
輸出結果:
iPone color is red
如果一個類繼承了多個類或接口,並且父類或接口中有相同的名字的方法需要重寫,那麼子類這時候必須重寫該方法,並且如果子類想區分父類中的方法,可以使用 super 關鍵字調用不同的父類方法。
/**
* 類中的 open 關鍵字標識該類可以被繼承
*/
open class Fly {
/**
* 方法中的 open 關鍵字標識該方法可以被重寫
*/
open fun show() {
println("Fly ......")
}
}
/**
* interface 不用寫 open, 默認就是 open 的
*/
interface Run {
/**
* 接口中的方法默認是 open 的
*/
fun show() {
println("Run .....")
}
}
/**
* 子類實現接口時不用寫 (), 因爲接口是沒有構造函數的
*/
class Bird : Fly(), Run {
override fun show() {
super<Fly>.show() // 使用 super 關鍵字來調用不同父類中的同名方法
super<Run>.show()
}
}
輸出結果:
Fly ......
Run .....
重寫變量
- 基類中被重寫的變量也要有 open 關鍵字的聲明,
- 子類可以使用 var 類型的變量去重寫父類中 val 類型的變量,但是不能使用 val 類型的變量去重寫父類中 var 類型的變量(如果使用 val 類型的變量去重寫父類的 var 類型的變量,那麼子類這個 val 類型的變量就會多一個 set 方法,而 val 類型的變量是不允許有 set 方法的)
open class Car(maxSpeed: Int, price: Int) {
open val maxSpeed: Int = maxSpeed
open var price: Int = price
}
class Maserati(maxSpeed: Int, price: Int) : Car(maxSpeed, price) {
/**
* 子類使用 var 類型重寫父類中的 val 類型的變量
*/
override var maxSpeed: Int = maxSpeed
/**
* 子類使用 val 類型重寫父類中的 var 類型的變量,========>>>>>>編譯報錯
*/
override val price: Int = price
}