Kotlin中的枚举类和Java中的枚举类型非常相似,具有类的特性。一般将可枚举的同类型的一组值作为枚举类定义。
Kotlin中,枚举类型以类的形式存在,因此叫做枚举类,如下是实例:
enum class Color {
RED, GREEN, BLUE
}
Kotlin中一切都是对象,因此,每一个枚举类型也是对象,多个枚举类型之间用 , 隔开。
先看下如何使用:
var color: Color = Color.BLUE
var color2 = Color.GREEN
// 比较两个枚举类型变量
var bool: Boolean = color == color2
需要注意,引用枚举类中的值,需要加上枚举类名。默认状态下,直接输出枚举类的元素值,会输出元素值的名称。
其实枚举类每一个值就是当前枚举类的对象,因此,如果要为每一个枚举类的值指定一个数字,直接通过构造器传入即可。
enum class Direction private constructor(var value: Int) {
NORTH(1), WEST(2), EAST(3), SOUTH(4)
}
首先说明一点,为每一个枚举值指定一个值,这个数并不一定从0开始,也不一定是按顺序的,因此枚举值在枚举类中的位置和枚举值对应的数值可能并不相同。
无论是Java还是Kotlin都提供了相应的API来获取枚举值的名字和索引。Kotlin提供了name和ordinal属性,分别用于获取枚举值名和索引。
println(color.name) // 返回枚举的名字
println(color.ordinal) // 返回枚举的索引
除此以外,还可以使用valueOf方法传入枚举值名称来获取枚举值对应的数值。
扩展是Kotlin中非常重要的功能,通过扩展,可以在没有源代码的情况下向类中添加成员。也可以在团队开发的情况下,通过扩展,将功能模块分散给多个人开发。
尽管JDK和Kotlin原生提供了很丰富的API,但这些API似乎仍然不够用,就需要为这些Library添加额外的API。
在类的外部对系统的类进行扩展,由于将扩展的部分都放到了自己的源代码中,因此这些源代码仍然可以运行在其他机器的JDK和Kotlin运行时上。Kotlin既可以对JDK的API进行扩展,也可以对Kotlin进行扩展。
fun ArrayList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
上面的代码是对原生集合类ArrayList进行扩展,扩展了swap交换方法。
这段代码放到哪个Kotlin文件中都可以,一般会放到Kotlin文件顶层,当然,也可以放在调用swap方法的前面。
扩展类的目的右很多,除了系统类需要扩展外,我们自己编写的类也需要扩展,扩展自定义类的方法和扩展系统类相同:
open class Parent(var value1: Int, var value2: Int) {
var mVaule1 = value1
var mValue2 = value2
fun add(): Int {
return mVaule1 + mValue2
}
}
class Child(value1: Int, value2: Int) : Parent(value1, value2) {
fun sub(): Int {
return value1 - value2
}
}
// 通过扩展向Parent类添加一个输出结果方法
fun Parent.printlnResult() {
println("${value1}+${value2}=${add()}")
}
fun Child.printlnResult() {
println("${value1}-${value2}=${sub()}")
}
因为open布恩那个用在顶层函数中,所以通过扩展是不能添加可继承的成员函数的(Kotlin默认不允许继承)。
如果通过扩展向类中添加的成员函数与类中原来的成员函数的结构完全相同,那么哪个优先呢?
答案是:内部成员函数的优先级更高,因此,通过扩展方法无法覆盖内部成员函数。
扩展属性和扩展方法类似,Kotlin属性在类中必须初始化,而初始化需要使用backing field,也就是那个field字段,可以将属性设置的值保存在field中,也可以从field获得属性值。但是通过扩展的属性是没有backing field的,因此,扩展属性需要实现setter部分,这样才能为扩展属性初始化。
class MClass {
var mValue: Int = 0
var str: String = ""
get() = str
set(value) {
field = value
}
}
// 属性扩展
var MClass.newVaule: Int
get() = mValue
set(value) {
mValue = value
}
由于扩展属性属性没有backing field字段,因此保存和获取属性值,需要使用一个类成员变量。但成员变量需要声明为public,否则扩展属性无法访问。
伴随对象:解决Kotlin类中没有静态成员所带来的尴尬。
如果类中右伴随对象,可以利用扩展为伴随对象添加成员。
class MClas {
companion object {
}
}
fun MClas.Companion.func() {
println("伴随对象成员函数")
}
以上编写的扩展代码都是在同一个包的同意个kotlin文件中,当然,同一个包的不同Kotlin文件中也是一样的,但是如果在不同包中的Kotlin里,就要使用import导入相应的资源了。之前介绍过import导包,就不再累赘了。
其实,扩展也可以在类中定义。
class D {
fun bar() {
println("D.bar")
}
}
class C {
fun baz() {
println("C.baz")
}
// 在类中对另一个类进行方法扩展
fun D.foo() {
bar()
baz()
}
fun caller(d: D) {
d.foo()
}
}
如果在B中扩展A,那么在A的扩展方法中调用A和B都有的成员函数,到底是调用A的还是B的呢?如下:
class A {
fun func() {
println("A.func")
}
}
class B {
fun func() {
println("B.func")
}
fun A.doo() {
func() // 调用A类的func方法
this@B.func() // 调用B类的func方法
}
fun call(a: A) {
a.doo()
}
}
如果在A.doo中调用B的func函数,需要使用this@B.func(),而调用A类中的方法可以直接使用。
以前提到过,扩展成员是不能被继承的,其实这个说法是不准确的。更准确的说法是:卸载顶层的扩展成员不能被继承,因为无法添加open关键字修饰。但在类中对另一个类扩展却可以添加open关键字。
尽管枚举类并不是在代码中经常出现,但用来定义可枚举的一组相关值还是非常好的,至少让代码变得更可读(远比使用常量或直接使用数字要好)。而扩展在很多语言中都支持,充分利用Kotlin扩展,可以让代码变得更容易维护,同时也带来了更大的灵活性。