類的宣告
和Java沒有什麼兩樣,Kotlin中,類的宣告也使用class關鍵字,如果只是宣告一個空類,Kotlin和Java沒有任何區別,不過定義類的其他成員,區別就很大了。
class MyClass{
}
複製程式碼
類的構造器
構造器也叫構造方法,是類建立的必要元素。
1、主構造器
面嚮物件語言在定義類的時候,都需要至少制定一個構造方法,如果不指定構造器,編譯器會預設生成一個不帶任何引數的構造器,這是傳統面嚮物件語言的做法。
Kotlin會有一些不一樣的地方,在Kotlin中,類允許定義一個主構造器,和若干個第二構造器。主構造器是類頭的一部分,緊跟在類名的後面,引數是可選的。如下程式碼定義了一個類,並指定了一個主構造器。
class Person constructor(name: String) {
}
複製程式碼
如果主構造器沒有任何註解,任何修飾,constructor可以省略:
class Person(name: String) {
}
複製程式碼
上面的程式碼只是宣告瞭主構造器,是在哪裡實現構造器呢?如果是主構造器,需要在init程式碼塊中進行初始化主構造器:
class Person(name: String) {
val myName = name
init {
println(name)
}
}
複製程式碼
注意:主構造器中的引數不僅可以在init程式碼塊中使用,還可以對類的屬性進行初始化。
var和val也可以修飾主構造器引數,如果使用var,表示引數對於構造器來說是變數,在構造器內部可以對其進行操作和改變;如果使用val,表示該引數是常量,在構造器中不能修改它的值。但要注意的是,var修飾的引數,在主構造器中修改值後,並不會把修改後的值傳到物件外面。
class Person(var name: String) {
init {
name="haha"
println(name)
}
}
複製程式碼
2、第二構造器
Kotlin的類中,除了可以宣告一個主構造器之外,還可以宣告若干個第二構造器,第二構造器必須在類中宣告,前面必須加constructor關鍵字。
class Person(var name: String) {
init {
name = "hello"+ name
println(name)
}
constructor(age: Int) : this("js") {
println(name + " " + age)
}
constructor(sex : Byte) :this(20){
println(name +" "+ sex)
}
}
複製程式碼
明瞭主構造器,那麼所有的第二構造器必須在宣告的後面呼叫主構造器,或者通過另外一個第二構造器間接地呼叫主構造器。
當然,如果類中並沒有宣告主構造器,第二構造器後面可以不呼叫主構造器。
上面Person類中一共定義了三個構造器,就涉及到了構造器的過載,也就是一個類中擁有不同個引數和引數型別的構造器。
注意:主構造器中可以使用var和val修飾引數,但第二構造器中不能使用,也就意味著第二構造器中的引數都是隻讀的。
3、Kotlin的單例模式
我們只是大概瞭解以下Kotlin的單例模式如何書寫,後面會詳細介紹。
class Singleton private constructor() {
public var value: Singleton? = null
private object mHolder {
val INSTANCE = Singleton()
}
companion object Factory {
fun getInstance(): Singleton {
return mHolder.INSTANCE
}
}
}
複製程式碼
4、函式中的預設引數
有很多變成語言是支援預設引數的,也就是在呼叫函式的時候不指定引數值,就會使用預設的引數值。Java不支援,但Kotlin是支援的,先看個例子:
class Class(param: Int, param1: String = "js") {
}
複製程式碼
在建立Class物件時,可以只傳第一個引數,第二個引數可以不傳,如Class(5),其第二個引數預設為"js"。
由於Kotlin支援預設引數,所以沒有必要非要定義一個沒有引數的構造器,可以直接定義一個所有引數都有預設值的構造器。
5、建立類例項
建立Kotlin例項在之前已經用到過了。對於大都數物件導向的語言來說,建立類例項的時候都會用到new關鍵字,但Kotlin中不再需要,直接呼叫構造器即可,如MyClass()。
類成員
1、屬性的基本用法
Java開發者對JavaBean一定特別熟悉,其實JavaBean就是一個普通的Javak類,關鍵在於對屬性的get和set方法。當然Java可以直接使用public的成員變數來解決這個問題,但對於屬性來說,不僅僅能讀寫其值,還需要對其進行二次加工,所以get/set也是必須的。
Kotlin中的屬性語法,只有var/val和屬性名時必須的,其他都是可選的。也就是說,Kotlin屬性最簡單的形式就是在類中定義一個變數(var)或常量(val),要引用屬性,就像引用變數一樣。
class Class{
var name:String="js"
val int:Int=2
fun pri(){
println("name=${name} int=${int}")
}
}
複製程式碼
2、屬性的get/set方法
因為Kotlin支援屬性語法,所以並不需要對每個單獨定義get和set方法。如果屬性只是可讀的(val),只需新增一個get方法,如果屬性是讀寫的,新增get和set方法。g如果get/set方法只有一行程式碼,直接用=分隔即可,如果有多行程式碼,則z使用{}處理:
class Class {
var name: String
get() = name
set(value) {
name = value
}
val age: Int
get() = age
}
複製程式碼
3、儲存屬性值的欄位
Kotlin中可以使用filed識別符號當作成員變數使用,也就是通過filed讀寫屬性值:
class Class {
var name: String = "js"
get() = field
set(value) {
field = value
}
}
複製程式碼
4、函式
Kotlin中,函式既可以在類外部定義,也可以在類的內部定義。如果是前者,是全域性函式,如果是後者,是類成員函式。但無論定義在哪裡,語法都是一樣的。
說到構造器時,構造器支援預設引數值,實際上,函式也支援預設引數值。要注意的是,帶預設值的引數必須是最後幾個引數,也就是說,如果某個引數帶有預設值,那麼該引數後面的所有引數必須都有預設值:
class Class {
fun func(url: String, host: String = "www.baidu.com") {
}
}
複製程式碼
但是如果帶預設值的引數過多,在調的時候也會帶來一些麻煩,如下:
class Class {
fun func(url: String, host: String = "www.baidu.com", name: String = "haha") {
}
}
複製程式碼
當我想呼叫func方法,host引數使用預設值,而name引數自定義,就不得不在呼叫方法的時候將host引數顯示地傳"www.baidu.com"。為了解決這個問題,Kotlin允許使用命名引數傳遞引數值,所謂命名引數,就是在呼叫函式的時候指定形參名(host、name),這樣就可以直接為指定的引數傳值了,如:
Class().func("hh",name="hello")
複製程式碼
如果傳入函式的引數個數不固定,可以使用可變引數,可變引數用vararg關鍵字宣告:
class Class {
fun func(url: String, host: String = "www.baidu.com", name: String = "haha") {
}
fun func2(vararg names: String) {
for (name in names) {
println(name)
}
}
}
複製程式碼
很多時候,函式體中只有一行程式碼,用傳統的寫法就比較麻煩了,這時可以直接在函式宣告後加等號(=),後面直接跟程式碼,這種方式可以省略函式返回值型別:
class Class {
var value: Int = 1
fun func(url: String, host: String = "www.baidu.com", name: String = "haha") {
}
fun func2(vararg names: String) {
for (name in names) {
println(name)
}
}
fun func3() = value
}
複製程式碼
同時,在函式體內也可以定義函式,這種函式叫做本地函式,不再累贅。
5、巢狀類
所謂巢狀類,就是直接在n類內部再定義一個類。
class People {
class Student {
}
}
People.Student()
複製程式碼
內部類還可以用inner宣告,表示可以通過外部類的例項進行呼叫:
class People {
inner class Student {
}
}
People().Student()
複製程式碼
修飾符
Kotlind的修飾符一共四個:private、protected、internal和public。
- private:僅在類的內部可以呼叫;
- protected:類似private,但在子類中也可以訪問;
- internal:模組中的任何類都可以呼叫;
- public:任何類都可以訪問。
如果不指定修飾符,預設全是public的,這些修飾符可以用在普通的類中,也可以用在構造器中。
類的繼承
1、如何繼承
與Java不同,Kotlin類的繼承需要使用冒號(:),而Java使用extends。注意,冒號後面需要呼叫父類的構造器。
Kotlin和Java一樣,都是單繼承的,也就是說,一個Kotlin類只能有一個父類。要注意的是,Kotlin類預設是final的,也就是不允許繼承的,需要顯示地用open關鍵字宣告,表示此類可以被繼承。
open class School {
}
class MiddleSchool : School() {
}
複製程式碼
2、重寫方法
Kotlin中不僅類預設是不可以被繼承的,連方法預設也是不可以重寫的。因此,如果要在子類中重寫方法啊,那麼父類的對應方法必須用open修飾,而且要在子類重寫的方法前面加override關鍵字:
open class School {
open fun func() {
}
}
class MiddleSchool : School() {
override fun func() {
}
}
複製程式碼
如果一個方法已經被override修飾了,那麼這個方法已經就被重寫了,依然可以被它的子類所重寫。
3、重寫屬性
屬性的重寫與方法類似,被重寫的屬性也必須用open修飾,子類屬性必須用override修飾。不過要注意,val屬性可以被重寫為var屬性,但反過來不可以:
open class School {
open val name: String = "School"
open fun func() {
}
}
class MiddleSchool : School() {
override var name: String = "MiddleSchool"
override fun func() {
}
}
複製程式碼
介面
介面是另一個重要的物件導向元素,用於制定規範,強調物件是否具有某個功能。
Kotlin與Java類似,使用interface宣告介面,一個類可以實現多個介面,實現的方法和類繼承相同,而且,介面中的屬性和方法都是open的。
interface MyInterface {
fun func()
fun func2(): String {
return "js"
}
}
class MyClass : MyInterface {
override fun func() {
}
}
複製程式碼
上述程式碼中可以看出:實現介面與繼承父類類似,使用冒號(:),但後面不是呼叫構造方法,而是是指跟介面名;Kotlin中的介面的方法,允許包含預設方法體,對於這樣的額方法,子類實現介面時不一定必須實現該方法。
抽象類
抽象類和介面非常相似,抽象類不能被例項化,需要abstract關鍵字宣告,抽象類實現介面後,介面中沒有函式體的函式可以不重寫,介面中的這些方法自動被繼承到子類中,稱為抽象方法:
abstract class MyAbsClass{
abstract fun f()
}
複製程式碼
抽象方法沒必要用open宣告,因為抽象類本身就是可以被繼承的。
小結
Kotlin中的類e和介面與Java中的本質上沒有什麼兩樣,只不過Kotlin為了體現差異,加入了一些語法糖,如介面允許函式帶函式體,支援屬性,不支援靜態方法等。我們需要慢慢去熟悉它。
更多精彩內容,歡迎關注我的微信公眾號——Android機動車