前言

本文主要讲解kotlin枚举类和扩展


Kotlin文章列表

Kotlin文章列表: 点击此处跳转查看


目录

Kotlin基础(六) 枚举类和扩展


1.1 枚举类

1.1.1 枚举类的根本用法

Kotlin中的枚举类(enum class)用于界说一组具有预界说值的常量。它们在许多情况下都很有用,例如表示一组相关的选项、状况或命名常量调集。下面是Kotlin枚举类的根本用法:

  1. 声明枚举类:

    enum class Direction {
        NORTH, SOUTH, EAST, WEST
    }
    
  2. 运用枚举值:

    val direction = Direction.NORTH
    println(direction)  // 输出: NORTH
    
  3. 比较枚举值:

    val direction = Direction.NORTH
    if (direction == Direction.NORTH) {
        println("向北")
    }
    
  4. 遍历枚举值:

    for (enumValue in Direction.values()) {
        println(enumValue)
    }
    
  5. 获取枚举值的称号:

    val direction = Direction.NORTH
    println(direction.name)  // 输出: NORTH
    
  6. 获取枚举值的序号(从0开始):

    val direction = Direction.NORTH
    println(direction.ordinal)  // 输出: 0
    
  7. 运用枚举类的办法和特点:

    enum class Direction {
        NORTH {
            override fun printDescription() {
                println("这是北方")
            }
        },
        SOUTH {
            override fun printDescription() {
                println("这是南边")
            }
        },
        EAST {
            override fun printDescription() {
                println("这是东方")
            }
        },
        WEST {
            override fun printDescription() {
                println("这是西方")
            }
        };
        abstract fun printDescription()
    }
    val direction = Direction.NORTH
    direction.printDescription()  // 输出: 这是北方
    

这些是Kotlin枚举类的根本用法。

1.1.2 为枚举值指定对应的数值

在Kotlin中,你可认为枚举值指定对应的数值。默认情况下,每个枚举值会主动分配一个从0开始递加的整数值作为其数值。然而,你也能够显式地为枚举值指定自界说的数值。下面是为枚举值指定数值的几种办法:

  1. 根本数值分配:

    enum class Direction {
        NORTH, SOUTH, EAST, WEST
    }
    

    在这种情况下,NORTH的数值为0,SOUTH的数值为1,以此类推。

  2. 自界说数值分配:

    enum class Direction(val degrees: Int) {
        NORTH(0), SOUTH(180), EAST(90), WEST(270)
    }
    

    在这个比如中,每个枚举值都有一个degrees特点来存储对应的数值。NORTH的数值为0,SOUTH的数值为180,以此类推。

  3. 运用匿名类和特点来分配数值:

    enum class Direction {
        NORTH {
            override val degrees: Int = 0
        },
        SOUTH {
            override val degrees: Int = 180
        },
        EAST {
            override val degrees: Int = 90
        },
        WEST {
            override val degrees: Int = 270
        };
        abstract val degrees: Int
    }
    

    在这个比如中,每个枚举值都经过匿名类来界说,偏重写了degrees特点以指定数值。

你能够依据需求挑选合适的办法来为枚举值指定数值。这样,你就能够在需求运用枚举值数值的地方拜访这些数值。例如,关于第二个示例中的Direction枚举类,你能够经过direction.degrees来拜访特定枚举值的数值。

1.1.3 枚举类的其他功用

除了根本用法外,Kotlin枚举类还供给了其他一些功用,使其愈加灵敏和强大。以下是一些Kotlin枚举类的其他功用:

  1. 声明办法:
    枚举类能够声明办法,能够在每个枚举值上调用这些办法。例如:

    enum class Direction {
        NORTH {
            override fun getOpposite(): Direction = SOUTH
        },
        SOUTH {
            override fun getOpposite(): Direction = NORTH
        },
        EAST {
            override fun getOpposite(): Direction = WEST
        },
        WEST {
            override fun getOpposite(): Direction = EAST
        };
        abstract fun getOpposite(): Direction
    }
    

    每个枚举值都完成了getOpposite()办法,依据当前方向回来相反的方向。

  2. 完成接口
    枚举类能够完成接口。这使得你可认为枚举类的每个枚举值供给不同的完成。例如:

    interface Printable {
        fun print()
    }
    enum class Direction : Printable {
        NORTH {
            override fun print() {
                println("北方")
            }
        },
        SOUTH {
            override fun print() {
                println("南边")
            }
        },
        EAST {
            override fun print() {
                println("东方")
            }
        },
        WEST {
            override fun print() {
                println("西方")
            }
        };
    }
    

    在这个比如中,Direction枚举类完成了Printable接口,并为每个枚举值供给了不同的print()办法完成。

  3. 经过字符串获取枚举值:
    你能够经过枚举值的字符串称号来获取对应的枚举值。运用valueOf()函数完成这一点。例如:

    val direction = Direction.valueOf("NORTH")
    println(direction)  // 输出: NORTH
    

    这将回来枚举值NORTH

  4. 界说扩展函数和特点:
    你可认为枚举类界说扩展函数和特点,为枚举值增加额定的功用。例如:

    enum class Direction {
        NORTH, SOUTH, EAST, WEST
    }
    fun Direction.isVertical(): Boolean {
        return this == Direction.NORTH || this == Direction.SOUTH
    }
    val Direction.opposite: Direction
        get() = when (this) {
            Direction.NORTH -> Direction.SOUTH
            Direction.SOUTH -> Direction.NORTH
            Direction.EAST -> Direction.WEST
            Direction.WEST -> Direction.EAST
        }
    

    在这个比如中,isVertical()是一个扩展函数,用于判别枚举值是否为垂直方向。opposite是一个扩展特点,依据当前方向回来相反的方向。

这些是Kotlin枚举类的一些其他功用,它们使得枚举类愈加灵敏和可扩展。

1.2 扩展

1.2.1 扩展原生API

Kotlin供给了扩展函数和扩展特点的功用,使你能够扩展原生的API类,包括标准库和其他类库中的类。这使你能够在不修正原始类代码的情况下为这些类增加新的功用。下面是如安在Kotlin中扩展原生API的几种办法:

  1. 扩展函数:
    你可认为任何类界说扩展函数。要界说一个扩展函数,你需求在函数名前面加上接收者类型并运用点符号。例如,以下是为字符串类型扩展一个reverse()函数:

    fun String.reverse(): String {
        return this.reversed()
    }
    

    运用示例:

    val reversedString = "Hello, World!".reverse()
    println(reversedString)  // 输出: "!dlroW ,olleH"
    

    在这个比如中,咱们扩展了String类,增加了一个reverse()函数,以回转字符串的顺序。

  2. 扩展特点:
    与扩展函数类似,你还可认为类界说扩展特点。要界说一个扩展特点,你能够运用get()和(可选)set()函数。以下是为Int类型扩展一个isEven特点:

    val Int.isEven: Boolean
        get() = this % 2 == 0
    

    运用示例:

    val number = 6
    println(number.isEven)  // 输出: true
    

    在这个比如中,咱们扩展了Int类,增加了一个isEven特点,用于判别一个整数是否为偶数。

  3. 扩展标准库:
    Kotlin的标准库供给了许多常见类型的扩展函数和特点,使你能够以更简洁的办法处理数据。例如,标准库中的List类供给了许多便利的扩展函数,如filter()map()reduce()等,用于对列表进行操作和转换。

    val numbers = listOf(1, 2, 3, 4, 5)
    val evenNumbers = numbers.filter { it % 2 == 0 }
    val doubledNumbers = numbers.map { it * 2 }
    val sum = numbers.reduce { acc, value -> acc + value }
    

    在这个比如中,咱们运用了filter()函数筛选出偶数,运用map()函数将每个数字乘以2,运用reduce()函数计算数字的总和。

扩展原生API使你能够依据自己的需求为现有类增加新的功用,进步代码的可读性和可维护性。请注意,扩展函数和特点仅在调用它们的效果域内可见,而且它们不能拜访类的私有或受维护成员。

1.2.2 扩展自界说类

在Kotlin中,你不仅能够扩展原生的API类,还能够扩展自界说的类。这使你可认为自己的类增加新的功用或办法,以满意特定的需求。下面是如安在Kotlin中扩展自界说类的几种办法:

  1. 扩展函数:
    你可认为自界说的类界说扩展函数,以增加新的功用。要界说一个扩展函数,你需求在函数名前面加上接收者类型并运用点符号。例如,假定你有一个名为Person的自界说类,你可认为它增加一个greet()函数:

    class Person(val name: String)
    fun Person.greet() {
        println("Hello, $name!")
    }
    

    运用示例:

    val person = Person("John")
    person.greet()  // 输出: "Hello, John!"
    

    在这个比如中,咱们为Person类增加了一个greet()函数,以便在实例上调用该函数时打印出问候语。

  2. 扩展特点:
    与扩展函数类似,你还可认为自界说的类界说扩展特点。要界说一个扩展特点,你能够运用get()和(可选)set()函数。以下是为Person类扩展一个age特点:

    class Person(val name: String)
    val Person.age: Int
        get() = 30
    

    运用示例:

    val person = Person("John")
    println(person.age)  // 输出: 30
    

    在这个比如中,咱们为Person类增加了一个age特点,始终回来30。

  3. 扩展其他自界说类:
    你可认为任何自界说类界说扩展函数和特点,不管它们是你自己编写的类还是来自其他类库。只需按照相同的语法和规则创立扩展函数和特点即可。例如:

    class Rectangle(val width: Int, val height: Int)
    fun Rectangle.isSquare(): Boolean {
        return width == height
    }
    

    运用示例:

    val rectangle = Rectangle(5, 5)
    println(rectangle.isSquare())  // 输出: true
    

    在这个比如中,咱们为Rectangle类增加了一个isSquare()函数,用于判别矩形是否为正方形。

扩展自界说类使你能够在不修正原始类代码的情况下为它们增加新的功用。这样能够进步代码的可读性、可维护性和灵敏性。扩展函数和特点仅在调用它们的效果域内可见,而且它们不能拜访类的私有或受维护成员。

1.2.3 成员函数抵触的处理方案

在Kotlin中,当你扩展的类和方针类具有相同称号的成员函数时,会产生函数抵触。这种情况下,编译器无法确认应该运用哪个函数。为了处理这个问题,你能够采纳以下两种处理方案:

  1. 显式指定调用的函数:
    当产生函数抵触时,你能够运用类名作为限制符,显式指定调用哪个类的函数。例如,假定你有一个名为String的扩展函数toUpperCase(),可是String类自身也有一个toUpperCase()函数,你能够运用类名来调用扩展函数:

    fun String.toUpperCase(): String {
        return "Extension function"
    }
    fun main() {
        val str = "Hello"
        println(str.toUpperCase())         // 调用方针类的函数
        println(str.String.toUpperCase())  // 调用扩展函数
    }
    

    在这个比如中,咱们经过运用类名String作为限制符,显式调用了扩展函数toUpperCase()和方针类的函数toUpperCase()

  2. 运用@JvmName注解:
    另一种处理函数抵触的办法是运用@JvmName注解为扩展函数指定一个不同的Java虚拟机称号。这样能够避免函数称号抵触。例如:

    @file:JvmName("StringUtil")
    package com.example.utils
    fun String.toUpperCase(): String {
        return "Extension function"
    }
    

    在这个比如中,咱们运用@JvmName注解将扩展函数toUpperCase()的Java虚拟机称号设置为StringUtil.toUpperCase(),避免了与方针类的函数抵触。

    注意:@JvmName注解应该放在扩展函数地点的文件的顶部。

经过显式指定调用的函数或运用@JvmName注解,你能够处理函数抵触问题,并确保在扩展类和方针类具有相同称号的成员函数时,能够正确地调用所需的函数。

1.2.4 扩展特点

在Kotlin中,你能够经过扩展特点为类增加新的特点。扩展特点答应你为现有类增加新的特点,而无需修正其源代码。以下是如安在Kotlin中扩展特点的示例:

// 假定有一个名为Person的类
class Person(val name: String)
// 为Person类增加一个扩展特点age
val Person.age: Int
    get() = 30
fun main() {
    val person = Person("John")
    println(person.age)  // 输出: 30
}

在上述示例中,咱们为Person类增加了一个扩展特点age。扩展特点的界说类似于扩展函数,可是在这种情况下,咱们只需求界说get()函数来获取特点的值。

请注意以下事项:

  • 扩展特点不能有初始值,由于它们没有与之相关的字段。因此,你需求供给一个get()函数来计算特点的值。
  • 扩展特点不支持set()函数,因此它们是只读的。

与扩展函数一样,扩展特点仅在调用它们的效果域内可见,而且它们不能拜访类的私有或受维护成员。

经过运用扩展特点,你能够便利地为现有类增加新的特点,然后供给更灵敏的数据操作和拜访办法。

1.2.5 扩展随同目标(CompanionObject)

在Kotlin中,你能够运用扩展函数和扩展特点来扩展随同目标(companion object)。随同目标是与类相关的单例目标,它能够包括类级别的函数和特点。以下是怎么扩展随同目标的示例:

class MyClass {
    companion object {
        fun greet() {
            println("Hello from companion object!")
        }
    }
}
// 扩展随同目标的函数
fun MyClass.Companion.sayHello() {
    println("Hello from extension function of companion object!")
}
fun main() {
    MyClass.greet()      // 调用随同目标的函数
    MyClass.sayHello()   // 调用扩展函数
}

在上述示例中,咱们有一个包括随同目标的MyClass类。咱们能够经过直接运用类名调用随同目标的函数greet()

然后,咱们运用扩展函数的语法来为随同目标增加新的函数sayHello()。这样,咱们能够运用随同目标的函数和扩展函数来进行调用。

请注意以下事项:

  • 扩展随同目标的函数或特点的界说需求运用Companion关键字作为限制符。
  • 扩展随同目标的函数和特点能够与随同目标内部界说的函数和特点共存,它们在调用时运用相同的语法。

经过扩展随同目标,你可认为随同目标增加新的功用,而无需修正原始类的源代码。这使得你能够更灵敏地扩展随同目标并为其增加额定的行为。

1.2.6 扩展的规模

在Kotlin中,扩展的规模(可见性)是受限的,扩展函数和特点只在特定的效果域内可见。以下是关于Kotlin扩展规模的几点要注意的事项:

  1. 扩展函数和特点的效果域:

    • 在顶层:你能够在文件的顶层界说扩展函数和特点,它们将在整个文件中可见。
    • 在类内部:你能够在类内部界说扩展函数和特点,它们将在类的规模内可见。
  2. 可见性限制:

    • 扩展函数和特点不能拜访类的私有或受维护成员。它们只能拜访类中的公共成员。
    • 扩展函数和特点不会承继给子类。只要在扩展函数或特点界说地点的效果域内才可见,无法经过承继传递给子类。
  3. 导入扩展:

    • 要运用扩展函数和特点,你需求在运用它们的文件中导入它们地点的包或文件。
    • 如果扩展函数或特点在同一个包内,你不需求导入,能够直接运用。
    • 如果扩展函数或特点在不同的包内,你需求运用import关键字导入它们。

例如,假定有以下代码:

package com.example.utils
fun String.reverse(): String {
    return this.reversed()
}
class MyClass {
    fun doSomething() {
        val str = "Hello"
        println(str.reverse())
    }
}
fun main() {
    val str = "World"
    println(str.reverse())
}

在这个比如中,reverse()扩展函数坐落com.example.utils包中。在main()函数中,咱们需求导入reverse()函数地点的包才干运用它。而在MyClass类的doSomething()函数中,由于在同一个包内,所以无需导入即可运用。

总结起来,扩展函数和特点的规模是局限于界说它们的效果域内。你需求正确导入扩展函数和特点地点的包或文件,才干在其他地方运用它们。别的,扩展函数和特点无法拜访类的私有或受维护成员。

1.2.7 在类中运用扩展

在Kotlin中,你能够在类内部运用扩展函数和扩展特点。这样做可认为类增加额定的功用,而无需修正类的源代码。以下是如安在类中运用扩展的示例:

class MyClass {
    private val data: MutableList<String> = mutableListOf()
    fun addData(item: String) {
        data.add(item)
    }
    fun displayData() {
        println("Data: $data")
    }
    // 在类内部界说扩展函数
    fun MyClass.processData() {
        data.forEach {
            println("Processing: $it")
        }
    }
    // 在类内部界说扩展特点
    val MyClass.totalDataSize: Int
        get() = data.size
}
fun main() {
    val myObj = MyClass()
    myObj.addData("Item 1")
    myObj.addData("Item 2")
    myObj.addData("Item 3")
    myObj.displayData()              // 输出: Data: [Item 1, Item 2, Item 3]
    myObj.processData()              // 输出: Processing: Item 1, Processing: Item 2, Processing: Item 3
    println(myObj.totalDataSize)     // 输出: 3
}

在上述示例中,咱们在MyClass类内部界说了一个扩展函数processData()和一个扩展特点totalDataSize。这些扩展函数和特点能够直接在类的实例上运用。

请注意以下事项:

  • 在类内部界说的扩展函数和特点能够直接拜访类的私有成员。
  • 在类内部界说的扩展函数和特点只在类的实例上可见,不能在类的随同目标或其他效果域中拜访。

经过在类内部运用扩展函数和特点,你可认为类增加特定的功用,与类的其他成员一同工作,并直接在类的实例上运用它们。这样能够进步代码的可读性和可维护性,并使类的功用愈加灵敏。

1.2.8 调用特定类的成员函数

在Kotlin中,你能够运用限制符(qualifier)来调用特定类的成员函数。经过限制符,你能够清晰指定调用哪个类的成员函数,尤其在存在函数称号抵触或多个类具有相同函数名时很有用。以下是几种运用限制符调用特定类成员函数的办法:

  1. 运用类名作为限制符:

    class MyClass {
        fun myFunction() {
            println("Hello from MyClass")
        }
    }
    fun main() {
        val obj = MyClass()
        obj.myFunction()     // 调用MyClass的myFunction函数
    }
    

    在这个比如中,咱们经过创立MyClass的实例obj来调用它的成员函数myFunction()。经过实例目标限制符,咱们清晰地指定了调用的是MyClass的成员函数。

  2. 运用扩展函数:

    class MyClass {
        fun myFunction() {
            println("Hello from MyClass")
        }
    }
    fun MyClass.anotherFunction() {
        println("Hello from extension function")
    }
    fun main() {
        val obj = MyClass()
        obj.myFunction()         // 调用MyClass的myFunction函数
        obj.anotherFunction()    // 调用MyClass的扩展函数
    }
    

    在这个比如中,咱们在MyClass类外部界说了一个扩展函数anotherFunction()。经过在实例目标上调用该扩展函数,咱们仍然是清晰指定了要调用的是MyClass的函数。

运用限制符的好处是能够避免函数称号抵触,并确保在存在多个类具有相同函数名的情况下,调用的是正确的类的函数。

需求注意的是,限制符只能用于调用类的成员函数,无法用于调用类的扩展函数。扩展函数的调用需求运用扩展函数的界说效果域来限制,而不是运用限制符。

1.2.9 扩展成员的承继

在Kotlin中,扩展函数和特点是静态解析的,它们不会被子类承继。这意味着在子类中界说相同称号的函数或特点不会覆盖或承继父类的扩展函数或特点。相反,子类会具有自己独立的同名成员函数或特点。

让咱们看一个示例来阐明这一点:

open class Parent
fun Parent.printMessage() {
    println("Hello from Parent")
}
class Child : Parent()
fun Child.printMessage() {
    println("Hello from Child")
}
fun main() {
    val parent: Parent = Child()
    parent.printMessage()  // 输出: Hello from Parent
}

在这个比如中,咱们有一个Parent类和一个Child类,Child类承继自Parent类。咱们在Parent类中界说了一个扩展函数printMessage(),然后在Child类中界说了一个同名的函数。

当咱们经过Child类的实例来调用printMessage()函数时,实践上调用的是Parent类的扩展函数。这是由于扩展函数是静态解析的,它们的调用在编译时就确认了,而不是在运行时依据实践目标的类型进行动态分配。

因此,子类无法承继或覆盖父类的扩展函数或特点。扩展函数和特点仅在界说它们的效果域内可见,而且它们不能经过承继传递给子类。