Kotlin——中級篇(五):列舉類(Enum)、介面類(Interface)詳解

Jetictors發表於2017-12-17

Kotlin——中級篇(五):列舉類(Enum)、介面類(Interface)詳解

在上一章節中,詳細的類(class)做了一個例項講解,提到了類(class)的例項化、建構函式、宣告、實現方式、和Java中類的區別等。但是對於Kotlin中的類的使用還遠遠不止那些。並且在上文中提到了關於類的類別。故而這篇文章就詳細說一說Kotlin中的列舉類(Enum)、介面類(Interface)的使用。

目錄

Kotlin——中級篇(五):列舉類(Enum)、介面類(Interface)詳解

一、列舉類

1.1、宣告方式及列舉常量

  • 關鍵字:enum
  • 列舉常量:即列舉類下的物件,每個列舉類包含0個到多個列舉常量。

1.1.1、宣告

enum關鍵字在類頭中的class關鍵字前面

宣告格式:

enum class 類名{
      ...
}
複製程式碼

1.1.2、列舉常量

列舉類中的每一個列舉常量都是一個物件,並且他們之間用逗號分隔。

例:

/**
 * 例:關於一個網路請求結果的列舉類
 */
enum class State{
    /*
         NORMAL : 正常
         NO_DATA : 資料為空
         NO_INTERNET : 網路未連線
         ERROR : 錯誤
         OTHER : 其他
     */

    NORMAL,NO_DATA,NO_INTERNET,ERROR,OTHER
}
複製程式碼

1.1.3、訪問列舉常量

  • 不需要例項化列舉類就可以訪問列舉常量

使用方式為:

列舉類名.列舉常量.屬性
複製程式碼

通過上面例子來例項講解:

// 使用中綴符號訪問列舉常量
State.NORMAL.name
State.NO_DATA.name
State.NO_INTERNET.name
State.ERROR.name
State.OTHER.name
複製程式碼

這裡只是讓大家明白怎樣去訪問一個列舉常量。沒有講解到列舉常量的使用。列舉常量的使用請大家耐心的看下去。在下面會詳細介紹怎樣去使用它。

1.2 、列舉常量的初始化

  • 因為每一個列舉都是列舉類的例項,所以他們可以是初始化過的。

例:

 enum class Color(var argb : Int){
     RED(0xFF0000),
     WHITE(0xFFFFFF),
     BLACK(0x000000),
     GREEN(0x00FF00)
}
複製程式碼

1.3、列舉常量的匿名類

  • 要實現列舉常量的匿名類,則必須提供一個抽象方法(必須重寫的方法)。且該方法定義在列舉類內部。而且必須在列舉變數的後面。
  • 列舉變數之間使用逗號(,)分割開。但是最後一個列舉變數必須使用分號結束。不然定義不了抽象方法。
  • 在上面已經說過,每一個列舉常量就是一個物件。

例:

fun main(args: Array<String>) {
    ConsoleColor.BLACK.print()
}

enum class ConsoleColor(var argb : Int){
    RED(0xFF0000){
        override fun print() {
            println("我是列舉常量 RED ")
        }
    },
    WHITE(0xFFFFFF){
        override fun print() {
            println("我是列舉常量 WHITE ")
        }
    },
    BLACK(0x000000){
        override fun print() {
            println("我是列舉常量 BLACK ")
        }
   },
    GREEN(0x00FF00){
        override fun print() {
            println("我是列舉常量 GREEN ")
        }
    };

    abstract fun print()
}
複製程式碼

輸出結果為:

我是列舉常量 BLACK 
複製程式碼

1.4、列舉類的使用

  • 每個列舉常量都包含兩個屬性:name(列舉常量名)ordinal(列舉常量位置)
  • 提供了values()valueOf()方法來檢測指定的名稱與列舉類中定義的任何列舉常量是否匹配。
  • Kotlin 1.1起,可以使用 enumValues<T>()enumValueOf<T>()函式以泛型的方式訪問列舉類中的常量。

1.4.1、訪問列舉變數屬性

例:

fun main(args: Array<String>) {
    println("name = " + Color.RED.name + "\tordinal = " + Color.RED.ordinal)
    println("name = " + Color.WHITE.name + "\tordinal = " + Color.WHITE.ordinal)
    println("name = " + Color.BLACK.name + "\tordinal = " + Color.BLACK.ordinal)
    println("name = " + Color.GREEN.name + "\tordinal = " + Color.GREEN.ordinal)
}

enum class Color(var argb : Int){
     RED(0xFF0000),
     WHITE(0xFFFFFF),
     BLACK(0x000000),
     GREEN(0x00FF00)
}  
複製程式碼

輸出結果為:

name = RED	ordinal = 0
name = WHITE	ordinal = 1
name = BLACK	ordinal = 2
name = GREEN	ordinal = 3
複製程式碼

1.4.2、使用enumValues<T>()enumValueOf<T>()訪問

例: 列舉類還是上面例子中的Color

println(enumValues<Color>().joinToString { it.name })
println(enumValueOf<Color>("RED"))
複製程式碼

輸出結果為:

RED, WHITE, BLACK, GREEN
RED
複製程式碼

1.4.3、使用valueOf()values()檢測

例:

println(Color.valueOf("RED"))
println(Color.values()[0])
println(Color.values()[1])
println(Color.values()[2])
println(Color.values()[3])
複製程式碼

輸出結果為:

RED
RED
WHITE
BLACK
GREEN
複製程式碼

其中,若使用Color.valueOf("不存在的列舉常量"),則會丟擲IllegalArgumentException 異常,即列舉變數不存在。若使用Color.values()[大於列舉常量位置],則會丟擲下標越界異常。

1.5、列舉類的原始碼分析

Enum.kt這個原始檔。

在這裡我大致的說明一下這個原始檔的方法、屬性等。有興趣的可以去看看這個原始檔。其實裡面也沒幾個方法。

1.5.1、預設實現了companion object {}

這也是我們訪問列舉常量無需例項化列舉類的原因。

1.5.2、僅提供了兩個屬性

  • 即我們上面用到的列舉常量名稱(name)和列舉常量位置(ordinal)

貼上這兩個屬性的原始碼:

/**
 * Returns the name of this enum constant, exactly as declared in its enum declaration.
 */
public final val name: String

/**
 * Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant
 * is assigned an ordinal of zero).
 */
public final val ordinal: Int
複製程式碼

1.5.3、實現了Comparable介面

  • 這也是我們能獲取列舉常量位置的原因。

這是Enum.kt原始檔。讓大家看看它實現了Comparable介面

public abstract class Enum<E : Enum<E>>(name: String, ordinal: Int): Comparable<E>{
      ...
}
複製程式碼

再來看看Comparable.kt裡面做了些什麼。其實裡面就提供了一個方法罷了...

public interface Comparable<in T> {
    /**
     * Compares this object with the specified object for order. Returns zero if this object is equal
     * to the specified [other] object, a negative number if it's less than [other], or a positive number
     * if it's greater than [other].
     */
    public operator fun compareTo(other: T): Int
}
複製程式碼

關於列舉類的講解就寫到這裡了。不清楚的可以多看看文章,或者看看原始碼、官方文件等等。當然,自己按照我的例子去敲一遍程式碼也是非常不錯的。

二、介面類

2.1、介面的基礎使用

2.1.1、宣告

關鍵字:interface

定義格式:

interface 介面名{
    ...
}
複製程式碼

2.1.2、用法

  • 關鍵字:冒號(:),這一點是和Java不同的。Java中使用介面使用的是implements關鍵字
  • Kotlin中冒號(:)使用的地方很多:
    1. 用於變數的定義
    2. 用於繼承
    3. 用於介面
    4. 方法的返回型別宣告

使用格式:

class 類名 : 介面名{
    // 重寫的介面函式、屬性等
    ...
}
複製程式碼

2.1.3、舉例說明

fun main(args: Array<String>) {

   // 類的初始化
   var demo = Demo1()

   demo.fun1()
}

/**
 * 我定義的介面
 */
interface Demo1Interface{

    // 定義的方法
    fun fun1()
}

/**
 * 介面的實現類
 */
class Demo1 : Demo1Interface{
    override fun fun1() {
        println("我是介面中的fun1方法")
    }
}
複製程式碼

輸出結果為:

我是介面中的fun1方法
複製程式碼

2.2、介面中的方法使用

  • 不帶結構體的函式可以省略大括號,且不用強制重寫帶結構體的函式就可以直接呼叫。不太明白也沒關係,下面的程式碼中都有註釋。

例:

fun main(args: Array<String>) {
    var demo = Demo2()

    demo.fun1()
    demo.fun2(5)
    println(demo.fun3(10))
    println(demo.fun4())

    //可以不重寫該方法直接呼叫
    demo.fun5()
}

interface Demo2Interface{

    /**
     * 定義一個無引數無返回值的方法
     */
    fun fun1()

    /**
     * 定義一個有引數的方法
     */
    fun fun2(num: Int)

    /**
     * 定義一個有引數有返回值的方法
     */
    fun fun3(num: Int) : Int

    // 下面的兩個方法是有結構體, 故可以不重寫

    /**
     * 定義一個無引數有返回值的方法
     */
    fun fun4() : String{
        return "fun4"
    }

    /**
     * 定義一個無結構體函式,大括號是可以省略的
     */
    fun fun5(){
        // 如果函式中不存在表示式,大括號可以省略。
        // 如fun1一樣
    }
}

class Demo2 : Demo2Interface{

    override fun fun1() {
        println("我是fun1()方法")
    }

    override fun fun2(num: Int) {
        println("我是fun2()方法,我的引數是$num")
    }

    override fun fun3(num: Int): Int {
        println("我是fun3()方法,我的引數是$num,並且返回一個Int型別的值")
        return num + 3
    }

    override fun fun4(): String {
        println("我是fun4()方法,並且返回一個String型別的值")
   
        /*
            介面中的fun4()方法預設返回”fun4“字串.
            可以用super.fun4()返回預設值
            也可以不用super關鍵字,自己返回一個字串
        */
        return super.fun4()
    }

    /*
         介面中的fun5()帶有結構體,故而可以不用重寫,
         fun4()同樣
    */

    //    override fun fun5() {
    //        super.fun5()
    //    }
}
複製程式碼

輸出結果為:

我是fun1()方法
我是fun2()方法,我的引數是5
我是fun3()方法,我的引數是10,並且返回一個Int型別的值
13
我是fun4()方法,並且返回一個String型別的值
fun4
複製程式碼

2.3、介面中的屬性使用

  • 在介面中申明屬性。介面中的屬性要麼是抽象的,要麼提供訪問器的實現。介面屬性不可以有後備欄位。而且訪問器不可以引用它們。

2.3.1、作為抽象

  • 即重寫屬性的時候是在實現類的類引數中。這也是用程式碼提示去重寫的實現方法

例:

fun main(args: Array<String>) {
    var demo = Demo3(1,2)
    println(demo.sum())
}

interface Demo3Interface{

    val num1: Int

    val num2 : Int  
}

class Demo3(override val num1: Int, override val num2: Int) : Demo3Interface{
    fun sum() : Int{
        return num1 + num2
    }
}
複製程式碼

輸出結果為:

3
複製程式碼

2.3.2、作為訪問器

即手動方式去實現重寫,並提供get()方法

例:

fun main(args: Array<String>) {
    println(demo.result())

    // 在這裡也可以改變介面屬性的值
    demo.num4 = 10
    println(demo.result())
}

interface Demo3Interface{

     // 宣告比那倆和提供預設值
     // 注意: val num3: Int = 3  這種方式不提供,為直接報錯的
    val num3: Int
    get() = 3

    val num4: Int
}

class Demo3(override val num1: Int, override val num2: Int) : Demo3Interface{

    // 提供訪問器實現
    override val num3: Int
        get() = super.num3

    // 手動賦值
    override var num4: Int = 4

    fun result() : Int{
        return num3 + num4
    }
}
複製程式碼

輸出結果為:

7
13
複製程式碼

2.4、介面的衝突問題解決

  • 該問題是指當我們在父類中宣告瞭許多型別,有可能出現一個方法的多種實現。

例:

fun main(args: Array<String>) {

    // 類的初始化
    val demo = Demo4()

    demo.fun1()
    demo.fun2()
}

interface Demo4InterfaceOne{
    fun fun1(){
        println("我是Demo4InterfaceOne中的fun1()")
    }

    fun fun2(){
        println("我是Demo4InterfaceOne中的fun2()")
    }
}

interface Demo4InterfaceTwo{
    fun fun1(){
        println("我是Demo4InterfaceTwo中的fun1()")
    }

    fun fun2(){
        println("我是Demo4InterfaceTwo中的fun2()")
    }
}

class Demo4 : Demo4InterfaceOne,Demo4InterfaceTwo{

    override fun fun1() {
        super<Demo4InterfaceOne>.fun1()
        super<Demo4InterfaceTwo>.fun1()
    }

    override fun fun2() {
        super<Demo4InterfaceOne>.fun2()
        super<Demo4InterfaceTwo>.fun2()
    }

}
複製程式碼

說明:Demo4實現了Demo4InterfaceOneDemo4InterfaceTwo兩個介面,而兩個介面中都存在兩個相同方法名的方法。因此編譯器不知道應該選哪個,故而我們用super<介面名>.方法名來區分。

三、 總結

我個人是從事Android開發的,以前用Java語言開發APP時因為考慮到手機效能的問題幾乎用不到列舉的。因為列舉太消耗記憶體了。當然用Kotlin語言開發Android專案中是否要用到列舉去便利去解決一些問題,此待小生自己研究。但是開發服務端專案時,一些問題用列舉是非常便利性的。
對於介面類來說,它在一個專案中是重中之重的,對於專案中程式碼的耦合性、便利性都能用介面類去實現一個良好的專案架構,對專案後期的維護或者說重構來說,都能有良好的體現。可能很多Java開發者都深有體會

專案原始碼

如果各位大佬看了之後感覺還闊以,就請各位大佬隨便star一下,您的關注是我最大的動力。
我的個人部落格Jetictors
GithubJetictors

歡迎各位大佬進群共同研究、探索

QQ群號:497071402

Kotlin——中級篇(五):列舉類(Enum)、介面類(Interface)詳解

相關文章