Kotlin Enum和Sealed类

588 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

Kotlin Enum和Sealed类

在编程中,总是需要区分类型常量。过去,我们通过声明静态变量来做到这一点。这样的方法并没有错,但是有更好的方法可以做到这一点。在 Kotlin 语言中,实现这一目标的方法不止一种。

在本文中,我们将了解最常用的两种类类型:密封类枚举类

Sealed

密封类提供了类的层次结构,这些类具有我们只能在编译时声明的子类。

sealed class OsSealed {
    object Linux : OsSealed()
    object Windows : OsSealed()
    object Mac : OsSealed()
}

我们还可以添加一个构造函数参数来表示拥有操作系统的公司:

sealed class OsSealed(val company: String) {
    object Linux : OsSealed("Open-Source")
    object Windows : OsSealed("Microsoft")
    object Mac : OsSealed("Apple")
}
用法

使用密封类的一种众所周知的方法是 使用when表达式:

when (osSealed) {
    OsSealed.Linux -> println("${osSealed.company} - Linux Operating System")
    OsSealed.Mac -> println("${osSealed.company} - Mac Operating System")
    OsSealed.Windows -> println("${osSealed.company} - Windows Operating System")
}
功能

在密封类中声明函数非常简单。如果我们需要在所有子类中使用该函数,我们可以在主父类中声明它们,而如果我们想使用具有不同功能的特定函数,我们可以通过在任何对象或子类下创建一个函数来实现

sealed class OsSealed(val releaseYear: Int = 0, val company: String = "") {
    constructor(company: String) : this(0, company)
    object Linux : OsSealed("Open-Source") {
        fun getText(value: Int): String {
            return "Linux by $company - value=$value"
        }
    }
    object Windows : OsSealed("Microsoft") {
        fun getNumber(value: String): Int {
            return value.length
        }
    }
    object Mac : OsSealed(2001, "Apple") {
        fun doSomething(): String {
            val s = "Mac by $company - released at $releaseYear"
            println(s)
            return s
        }
    }
    object Unknown : OsSealed()

    fun getTextParent(): String {
        return "Called from parent sealed class"
    }
}

在父类中声明的函数可以直接调用而无需强制转换。另一方面,在子对象或子类中声明的函数必须通过显式转换为它们的等效类型来调用。

最好使用is运算符删除when修饰符中不必要的样板代码 。这样做将消除将osSealed参数的值转换为OsSealed.Linux对象或其他定义的对象或子类的需要:

assertEquals("Called from parent sealed class", osSealed.getTextParent())
when (osSealed) {
    is OsSealed.Linux -> assertEquals("Linux by Open-Source - value=1", osSealed.getText(1))
    is OsSealed.Mac -> assertEquals("Mac by Apple - released at 2001", osSealed.doSomething())
    is OsSealed.Windows -> assertEquals(5, osSealed.getNumber("Text!"))
    else -> assertTrue(osSealed is OsSealed.Unknown)
}

Enum

使用枚举类 将每个枚举常量与其父级关联起来。

enum class OsEnum {
    Linux,
    Windows,
    Mac
}

我们还可以向枚举类添加构造函数参数:

enum class OsEnum(val company: String) {
    Linux("Open-Source"),
    Windows("Microsoft"),
    Mac("Apple")
}
用法

密封类类似,我们可以使用 带有when表达式的枚举,这主要用于处理不同的场景,具体取决于传递的枚举常量:

when (osEnum) {
    OsEnum.Linux -> println("${osEnum.company} - Linux Operating System")
    OsEnum.Mac -> println("${osEnum.company} - Mac Operating System")
    OsEnum.Windows -> println("${osEnum.company} - Windows Operating System")
}
功能

有几种方法可以在枚举类中声明函数。我们可以将函数声明为*抽象函数并在每个枚举常量中覆盖它,或者我们可以在父枚举类中声明它并将该函数与任何枚举*常量**一起使用:

enum class OsEnum(val releaseYear: Int = 0, val company: String = "") {
    Linux(0, "Open-Source") {
        override fun getText(value: Int): String {
            return "Linux by $company - value=$value"
        }
    },
    Windows(0, "Microsoft") {
        override fun getText(value: Int): String {
            return "Windows by $company - value=$value"
        }
    },
    Mac(2001, "Apple") {
        override fun getText(value: Int): String {
            return "Mac by $company - released at $releaseYear"
        }
    },
    Unknown {
        override fun getText(value: Int): String {
            return ""
        }
    };
    abstract fun getText(value: Int): String
    fun getTextParent(): String {
        return "Called from parent enum class"
    }
}

由于我们使用了抽象函数,因此函数名称没有任何区别。因此,我们只需要使用传入的osEnum参数来访问实现的抽象函数,以及父声明的函数:

when (osEnum) {
    OsEnum.Linux -> assertEquals("Linux by Open-Source - value=1", osEnum.getText(1))
    OsEnum.Windows -> assertEquals("Windows by Microsoft - value=2", osEnum.getText(2))
    OsEnum.Mac -> assertEquals("Mac by Apple - released at 2001", osEnum.getText(3))
    else -> assertTrue(osEnum == OsEnum.Unknown)
}

差异性

enum主要用作相互关联的常量。它们也可以与一些父函数配对。

Sealed class 类似于enums ,但允许更多的自定义。如前所述,它们是enumabstract class的混合体。

假设我们要添加一个没有已知值或不需要实现的子类。

密封类中,我们可以根据需要简单地添加多个自定义构造函数。 此外,我们可以定义多个具有不同名称、参数和返回类型的函数。

然而,在枚举类中,我们不能在每个枚举常量中定义不同的函数。 因此,即使在我们的Unknown 枚举常量中,我们也必须实现该常量不需要的方法,并且在初始化LinuxWindows**枚举常量时我们还必须传递整数值 0。

\