1. 程式人生 > 其它 >Kotlin學習之介面、資料類和單例類

Kotlin學習之介面、資料類和單例類

技術標籤:筆記kotlinandroidkotlin

介面

介面是用於實現多型程式設計的重要組成部分。我們都知道,Java是單繼承結構的語言,任何一個類最多隻能繼承一個父類,但是卻可以實現任意多個介面,Kotlin也是如此。

我們可以在介面中定義一系列的抽象行為,然後由具體的類去實現。下面還是通過具體的程式碼來學習一下,首先建立一個Study介面,並在其中定義幾個學習行為。右擊包→New→Kotlin File/Class,在彈出的對話方塊中輸入“Study”,建立型別選擇“Interface”。

然後在Study介面中新增幾個學習相關的函式,注意介面中的函式不要求有函式體,程式碼如下所示:

interface Study {
    fun readBooks()
    fun doHomework()
}

接下來就可以讓Student類去實現Study介面了,這裡我將Student類原有的程式碼調整了一下,以突出繼承父類和實現介面的區別:

class Student(name: String, age: Int) : Person(name, age), Study {
    override fun readBooks() {
        println(name + " is reading.")
    }

    override fun
doHomework() { println(name + " is doing homework.") } }

熟悉Java的人一定知道,Java中繼承使用的關鍵字是extends,實現介面使用的關鍵字是implements,而Kotlin中統一使用冒號,中間用逗號進行分隔。上述程式碼表示Student類繼承了Person類,同時還實現了Study介面。另外介面的後面不用加上括號,因為它沒有建構函式可以去呼叫。

Study介面中定義了readBooks()和doHomework()這兩個待實現函式,因此Student類必須實現這兩個函式。

現在我們可以在main()函式中編寫如下程式碼來呼叫這兩個介面中的函式:

fun main() {
    val student = Student("Jack", 19)
    doStudy(student)
}

fun doStudy(study: Study) {
    study.readBooks()
    study.doHomework()
}

首先建立了一個Student類的例項,本來是可以直接呼叫該例項的readBooks()和doHomework()函式的,但是我沒有這麼做,而是將它傳入到了doStudy()函式中。doStudy()函式接收一個Study型別的引數,由於Student類實現了Study介面,因此Student類的例項是可以傳遞給doStudy()函式的,接下來我們呼叫了Study介面的readBooks()和doHomework()函式,這種就叫作面向介面程式設計,也可以稱為多型。

現在執行一下程式碼,結果如圖2.20所示。

Snipaste_2020-12-30_13-57-03

為了讓介面的功能更加靈活,Kotlin還增加了一個額外的功能:允許對介面中定義的函式進行預設實現。其實Java在JDK 1.8之後也開始支援這個功能了,因此總體來說,Kotlin和Java在介面方面的功能仍然是一模一樣的。

下面我們學習一下如何對介面中的函式進行預設實現,修改Study介面中的程式碼,如下所示:

interface Study {
    fun readBooks()

    fun doHomework() {
        println("do homework default implementation.")
    }
}

可以看到,我們給doHomework()函式加上了函式體,並且在裡面列印了一行日誌,成為它的預設實現。現在當一個類去實現Study介面時,只會強制要求實現readBooks()函式,而doHomework()函式則可以自由選擇實現或者不實現,不實現時就會自動使用預設的實現邏輯。

現在回到Student類當中,你會發現如果我們刪除了doHomework()函式,程式碼是不會提示錯誤的,而刪除readBooks()函式則不行。

接下來我們再學習一個和Java相比變化比較大的部分——函式的可見性修飾符。

熟悉Java的人一定知道,Java中有public、private、protected和default(什麼都不寫)這4種函式可見性修飾符。Kotlin中也有4種,分別是public、private、protected和internal,需要使用哪種修飾符時,直接定義在fun關鍵字的前面即可。

下面我詳細介紹一下Java和Kotlin中這些函式可見性修飾符的異同。

首先private修飾符在兩種語言中的作用是一模一樣的,都表示只對當前類內部可見。public修飾符的作用雖然也是一致的,表示對所有類都可見,但是在Kotlin中public修飾符是預設項,而在Java中default才是預設項。

protected關鍵字在Java中表示對當前類、子類和同一包路徑下的類可見,在Kotlin中則表示只對當前類和子類可見。Kotlin拋棄了Java中的default可見性(同一包路徑下的類可見),引入了一種新的可見性概念,只對同一模組中的類可見,使用的是internal修飾符。

影象說明文字

資料類與單例類

1.資料類

在一個規範的系統架構中,資料類通常佔據著非常重要的角色,它們用於將伺服器端或資料庫中的資料對映到記憶體中,為程式設計邏輯提供資料模型的支援。

資料類通常需要重寫equals()、hashCode()、toString()這幾個方法。

這裡我們新構建一個手機資料類,欄位就簡單一點,只有品牌和價格這兩個欄位。如果使用Java來實現這樣一個數據類,程式碼就需要這樣寫:

public class Cellphone {
    String brand;
    double price;

    public Cellphone(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Cellphone) {
            Cellphone other = (Cellphone) obj;
            return other.brand.equals(brand) && other.price == price;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return brand.hashCode() + (int) price;
    }

    @Override
    public String toString() {java
        return "Cellphone(brand=" + brand + ", price=" + price + ")";
    }
}

而同樣的功能使用Kotlin來實現就會變得極其簡單,右擊包→New→Kotlin File/Class,在彈出的對話方塊中輸入“Cellphone”,建立型別選擇“Class”。然後在建立的類中編寫如下程式碼:

data class Cellphone(val brand: String, val price: Double)

你沒看錯,只需要一行程式碼就可以實現了!神奇的地方就在於data這個關鍵字,當在一個類前面聲明瞭data關鍵字時,就表明你希望這個類是一個數據類,Kotlin會根據主建構函式中的引數幫你將equals()、hashCode()、toString()等固定且無實際邏輯意義的方法自動生成,從而大大減少了開發的工作量。

另外,當一個類中沒有任何程式碼時,還可以將尾部的大括號省略。

下面我們來測試一下這個資料類,在main()函式中編寫如下程式碼:

fun main() {
    val cellphone1 = Cellphone("Samsung", 1299.99)
    val cellphone2 = Cellphone("Samsung", 1299.99)
    println(cellphone1)
    println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}

這裡我們建立了兩個Cellphone物件,首先直接將第一個物件打印出來,然後判斷這兩個物件是否相等。執行一下程式,結果如圖所示。

在這裡插入圖片描述

2.單例類

想必你一定聽說過單例模式吧,這是最常用、最基礎的設計模式之一,它可以用於避免建立重複的物件。比如我們希望某個類在全域性最多隻能擁有一個例項,這時就可以使用單例模式。當然單例模式也有很多種寫法,這裡就演示一種最常見的Java寫法吧:

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void singletonTest() {
        System.out.println("singletonTest is called.");
    }
}

這段程式碼其實很好理解,首先為了禁止外部建立Singleton的例項,我們需要用private關鍵字將Singleton的建構函式私有化,然後給外部提供了一個getInstance()靜態方法用於獲取Singleton的例項。在getInstance()方法中,我們判斷如果當前快取的Singleton例項為null,就建立一個新的例項,否則直接返回快取的例項即可,這就是單例模式的工作機制。

而如果我們想呼叫單例類中的方法,也很簡單,比如想呼叫上述的singletonTest()方法,就可以這樣寫:

Singleton singleton = Singleton.getInstance();
singleton.singletonTest();

雖然Java中的單例實現並不複雜,但是Kotlin明顯做得更好,它同樣是將一些固定的、重複的邏輯實現隱藏了起來,只暴露給我們最簡單方便的用法。

在Kotlin中建立一個單例類的方式極其簡單,只需要將class關鍵字改成object關鍵字即可。現在我們嘗試建立一個Kotlin版的Singleton單例類,右擊包→New→Kotlin File/Class,在彈出的對話方塊中輸入“Singleton”,建立型別選擇“Object”,點選“OK”完成建立,初始程式碼如下所示:

object Singleton {
}

現在Singleton就已經是一個單例類了,我們可以直接在這個類中編寫需要的函式,比如加入一個singletonTest()函式:

object Singleton {
    fun singletonTest() {
        println("singletonTest is called.")
    }
}

可以看到,在Kotlin中我們不需要私有化建構函式,也不需要提供getInstance()這樣的靜態方法,只需要把class關鍵字改成object關鍵字,一個單例類就建立完成了。而呼叫單例類中的函式也很簡單,比較類似於Java中靜態方法的呼叫方式:

Singleton.singletonTest()

這種寫法雖然看上去像是靜態方法的呼叫,但其實Kotlin在背後自動幫我們建立了一個Singleton類的例項,並且保證全域性只會存在一個Singleton例項。

()這樣的靜態方法,只需要把class關鍵字改成object關鍵字,一個單例類就建立完成了。而呼叫單例類中的函式也很簡單,比較類似於Java中靜態方法的呼叫方式:

Singleton.singletonTest()

這種寫法雖然看上去像是靜態方法的呼叫,但其實Kotlin在背後自動幫我們建立了一個Singleton類的例項,並且保證全域性只會存在一個Singleton例項。

本文章參考自郭霖的第一行程式碼——Android(第3版)