kotlin密封类sealed

一. 概念

Kotlin 中的密封类(Sealed Class)是一种特殊的类,用于限制类的继承结构。密封类允许你定义一组相关的类,并且只允许这些类作为其子类存在。这种限制可以在编译时检查,从而减少了程序出错的可能性。

语法如下:

sealed class MyClass {
    
    
    // ...
}

密封类通常用于代替枚举类。密封类的优点在于可以更灵活地定义子类,而枚举类的每个成员都是固定的。密封类还可以帮助你编写更加类型安全的代码

二. 应用场景

密封类的主要应用场景是在需要表示有限数量的类型的情况下。当你知道一个类的所有可能的子类时,可以使用密封类来表示这些子类。

例如,如果你有一个表示不同任务状态的密封类 TaskState,你可以编写一个 when 表达式来处理不同的状态:

/**
 * 密封类: 任务的状态
 */
sealed class SealedTaskState {
    
    
    // 空闲
    object EMPTY : SealedTaskState()

    // 执行中
    object RUNNING : SealedTaskState()

    // 暂停中
    object PAUSE : SealedTaskState()
}

// 单元测试
@Test
fun test(sealedTaskState: SealedTaskState) {
    
    
    when (sealedTaskState) {
    
    
        // SealedTaskState有3种状态
        // 下面的数据必须包含所有的SealedTaskState里的状态,不然编译器会报错
        
        is SealedTaskState.EMPTY -> {
    
    
            println("空闲")
        }
        is SealedTaskState.RUNNING -> {
    
    
            println("执行中")
        }
        is SealedTaskState.PAUSE -> {
    
    
            println("暂停")
        }
    }
}

上面的示例中,使用了密封类封装了任务的状态,when处理了SealedTaskState所有状态发生的情况。

三. 和普通类的区别

class SealedTest {
    
    

    // 密封类测试
    @Test
    fun testSealedShape(sealedShape: SealedShape) {
    
    
        when (sealedShape) {
    
    
            is SealedCircle -> println("circle area:${
      
      sealedShape.getArea()}")
            // 'when' expression must be exhaustive, add necessary 'is SealedRectangle' branch or 'else' branch instead
            // 直接编译报错,提示必须要SealedRectangle分支或者else分支
            // is SealedRectangle -> println("rectangle area : ${sealedShape.getArea()}")
        }
    }

    // 普通类测试
    @Test
    fun testNormalShape(normalShape: NormalShape) {
    
    
        when (normalShape) {
    
    
            is NormalCircle -> println("circle area:${
      
      normalShape.getArea()}")
            // 可能出现漏掉判断的情况,比如这里,我们把普通类矩形注释了,IDE也没有提示报错
            // 但是在密封类中,在编译期我们就能看到错误
            // is NormalRectangle -> println("rectangle area : ${normalShape.getArea()}")
        }
    }
}

/**
 * 普通类: 形状
 */
abstract class NormalShape {
    
    
    // 计算面积
    abstract fun getArea(): Double
}

// 普通类: 圆形
class NormalCircle(private val radius: Double) : NormalShape() {
    
    
    override fun getArea(): Double {
    
    
        return Math.PI * radius * radius
    }
}

// 普通类: 矩形
class NormalRectangle(private val width: Double, private val height: Double) : NormalShape() {
    
    
    override fun getArea(): Double {
    
    
        return width * height
    }
}



/**
 * 密封类: 形状
 */
sealed class SealedShape {
    
    
    // 计算面积
    abstract fun getArea(): Double
}

// 密封类: 圆形
class SealedCircle(private val radius: Double) : SealedShape() {
    
    
    override fun getArea(): Double {
    
    
        return Math.PI * radius * radius
    }
}

// 密封列: 矩形
class SealedRectangle(private val width: Double, private val height: Double) : SealedShape() {
    
    
    override fun getArea(): Double {
    
    
        return width * height
    }
}

首先,没有使用密封类NormalShape的情况下,如果我们添加了新的形状类型,例如梯形或正方形,那么我们必须修改 testSealedShape 函数,以处理这些新类型的形状。其次,如果我们在 when 表达式中遗漏了某种类型的形状,那么我们将无法在编译时或运行时捕获这个错误,这可能会导致程序在处理该类型的形状时出现异常。

由于 SealedShape 是一个密封类,它的子类是有限的,编译器可以确保 when 表达式处理了所有可能的情况。如果我们添加了一个新的图形类型,我们需要添加一个新的密封子类,并实现 getArea() 方法。这个过程可以在编译时检测到错误,而不是在运行时才发现错误。这就是密封类的优势之一。

四. 总结

从上面的示例说明来看,密封类的优势可以总结为以下几点:

  1. 编译时检查:由于密封类的子类是有限的,因此编译器可以检查 when 表达式是否涵盖了所有可能的情况。这可以避免在运行时才发现未处理的情况,从而提高代码的可靠性和安全性。
  2. 类层次结构的扩展能力:通过添加新的密封子类,可以轻松地扩展现有的类层次结构,而无需修改现有的代码。这可以提高代码的可维护性和可扩展性。
  3. 更清晰的代码:密封类可以使代码更加清晰和易于理解。它可以帮助程序员更好地组织代码,从而使其更容易理解和修改。

总的来说,密封类是一种非常有用的语言特性,可以提高代码的可靠性、可维护性和可扩展性。如果您需要处理有限数量的可能情况,并且希望在编译时检测到未处理的情况,那么密封类是一个很好的选择。

猜你喜欢

转载自blog.csdn.net/flytosky21/article/details/130142387