Kotlin 枚举类(enum)& 密封类(sealed)

我们一般使用枚举类如下所示

枚举 - enum 示例:

enum class Weekday(val id: Int, val value: String, val display: String){
    MON(0, "1", "周一"),
    TUE(1, "2", "周二"),
    WED(2, "3", "周三"),
    THU(3, "4", "周四"),
    FRI(4, "5", "周五"),
    SAT(5, "6", "周六"),
    SUN(6, "7", "周日");

    companion object {
        private val map = values().associateBy(Weekday::ordinal)
        fun fromId(id: Int) = map[id] ?: SUN
        fun fromValue(value: String?): Weekday? = when (value) {
            MON.value -> MON
            TUE.value -> TUE
            WED.value -> WED
            THU.value -> THU
            FRI.value -> FRI
            SAT.value -> SAT
            SUN.value -> SUN
            else -> SUN
        }
    }
}

枚举类转 密封类 - sealed 示例:

sealed class Weekday(val id: Int, val value: String, val display: String) {
    object Mon : Weekday(0, "1", "周一")
    object Tue : Weekday(1, "2", "周二")
    object Wed : Weekday(2, "3", "周三")
    object Thu : Weekday(3, "4", "周四")
    object Fri : Weekday(4, "5", "周五")
    object Sat : Weekday(5, "6", "周六")
    object Sun : Weekday(6, "7", "周日")

    companion object {
        fun fromId(id: Int) = Weekday::class.sealedSubclasses
            .mapNotNull { it.objectInstance }
            .firstOrNull { it.id == id } ?: Mon

        fun fromValue(value: String) = Weekday::class.sealedSubclasses
            .mapNotNull { it.objectInstance }
            .firstOrNull { it.value == value } ?: Mon
    }
}

在网上看到一篇写的很详细的回答两者之前的区别:sealed class vs enum

#1.特性

enum:每个枚举值不能有自己的唯一属性。被迫为每个枚举值拥有相同的属性:

enum class Week(val display: String?) {
    WEEKDAY("Monday"),
    WEEKEND(null),
}

sealed:我们可以为每个子类型设置不同的属性:

sealed class Week
class Weekday(val display: String) : Week()
object Weekend : Week()

#2.功能

enum:枚举可以具有抽象函数以及常规函数。但是和属性一样,每个枚举值也必须具有相同的功能:

enum class Week {
    WEEKDAY {
        override fun isRest() = false
    },
    WEEKEND {
        override fun isRest() = true
    };

    abstract fun isRest(): Boolean
}

用法:

class WeekManager {
    fun isRest(status: Week): Boolean {
        return status.isRest()
    }
}


sealed:我们可以为不同的子类型使用不同的函数:

sealed class Week
class Weekday : Week(){
    fun isWork()= true
}
class Weekend : Week(){
    fun isRest()= true
}

这里我们有不同的函数:isWork() 用于Weekday,isRest() 用于Weekend。这使意图更清晰并使代码更具可读性。

用法:

class WeekManager {
    fun displayText(day: Week): String = when(day) {
        is Weekday -> day.isWork()
        is Weekend -> day.isRest()
    }
}

如果我们想要所有子类型的通用函数,就和在枚举示例中一样写法就行。

#3.继承

enum:类是隐式 final 的,不能被其他类扩展,只能扩展接口:

interface Workable {}
enum class Week : Workable {}

sealed:不仅本身可以扩展,而且可以扩展其他类以及接口:

sealed class Week : WorkStatus(){}
sealed class Week : Workable{}
class Weekday : Week(){}

#4.可序列化和可比较

enum:都由抽象类 java.lang.Enum 隐式扩展。因此,所有枚举值具有 equals()、toString()、hashCode()、Serializable 和 Comparable 的实现。我们不必定义它们。

sealed:我们需要手动定义它们,或者使用数据类自动实现 equals()、toString() 和 hashcode(),然后手动实现 Serializable 和 Comparable。

#5.性能

enum:

1.枚举不会被垃圾收集,它们会在您的应用程序的整个生命周期中保留在内存中。这可能是有利的,也可能是不利的。

a.优点:垃圾收集过程是昂贵的。对象创建也是如此,我们不想一次又一次地创建相同的对象。因此,使用枚举,您可以节省垃圾收集和对象创建的成本。这是好处。

b.缺点:枚举即使在不使用时也会留在内存中,这会使内存一直被占用。

⚠️ 如果应用程序中有 100 到 200 个枚举,则无需担心所有这些。但是,当您拥有更多数量时,您可以根据事实来决定是否应该使用枚举,例如枚举的数量、它们是否会一直使用以及分配给 JVM 的内存量。

2.在 when 表达式中枚举值的比较会更快,因为在幕后,它使用 tableswitch 来比较对象。

3.在 Android 中,当启用优化时,Proguard 会将没有函数和属性的枚举转换为整数,因此您可以在编译时获得枚举的类型安全性,并在运行时获得整数的性能。

sealed:

1.密封类只是普通类,唯一的例外是它们需要在同一个包和同一个编译单元中进行扩展。所以,他们的表现相当于普通类。

2.密封类的子类型的对象像常规类的对象一样被垃圾收集。因此,您必须承担垃圾收集和对象创建的成本。

⚠️ 当您有低内存限制时,如果您需要数千个对象,您可以考虑使用密封类而不是枚举。因为垃圾收集器可以在对象不使用时释放内存。

4.如果您使用对象声明来扩展密封类,则对象将充当单例并且不会被垃圾收集,就像枚举一样。

5.密封类的类型比较在when表达式中比较慢,因为在后台它使用 instanceof 来比较类型。在这种情况下,枚举和密封类之间的速度差异很小。仅当您在循环中比较数千个常量时才重要。

你可能感兴趣的:(Android,enum,sealed)