轉向Kotlin——類和介面

Android機動車發表於2019-02-26

類的宣告

和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機動車

這裡寫圖片描述

相關文章