
在上一章節中,詳細的類(class)做了一個實例講解,提到了類(class)的實例化、構造函數、聲明、實現方式、和Java中類的區別等。但是對於Kotlin中的類的使用還遠遠不止那些。並且在上文中提到了關於類的類別。故而這篇文章就詳細說一說Kotlin中的枚舉類(Enum)、接口類(Interface)的使用。
如果還對Kotlin中的類還沒有一個清晰的概念及認識的朋友請閱讀我的上一篇博文:
目錄

一、枚舉類
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中冒號(:)使用的地方很多:
- 用於變量的定義
- 用於繼承
- 用於接口
- 方法的返回類型聲明
使用格式:
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實現了Demo4InterfaceOne和Demo4InterfaceTwo兩個接口,而兩個接口中都存在兩個相同方法名的方法。因此編譯器不知道應該選哪個,故而我們用super<接口名>.方法名來區分。
三、 總結
我個人是從事Android開發的,以前用Java語言開發APP時因為考慮到手機性能的問題幾乎用不到枚舉的。因為枚舉太消耗內存了。當然用Kotlin語言開發Android項目中是否要用到枚舉去便利去解決一些問題,此待小生自己研究。但是開發服務端項目時,一些問題用枚舉是非常便利性的。
對於接口類來說,它在一個項目中是重中之重的,對於項目中代碼的耦合性、便利性都能用接口類去實現一個良好的項目架構,對項目后期的維護或者說重構來說,都能有良好的體現。可能很多Java開發者都深有體會
如果各位大佬看了之后感覺還闊以,就請各位大佬隨便star一下,您的關注是我最大的動力。
我的個人博客:Jetictors
我的掘金:Jetictors
Github:Jetictors
