枚举类
当我们想表示一组固定的常量时,我们倾向于使用枚举。例如,一周中的几天、网络状态、颜色集等。
为什么我们首先使用枚举类?
如果要将变量限制为一组值,可以使用枚举。例如,如果我们有一个名为“day”的变量,并且希望用户从一周中的days-out-of-week枚举中选择一个值,那么我们将该变量限制为从给定的一组值中进行选择。
如何使用枚举类?
我们使用enum关键字后跟类的名称来定义enum类。
enum class Size {
SMALL,
MEDIUM,
LARGE,
}
我们还可以向enum类添加构造函数参数。
enum class Size(weight: Int) {
SMALL(weight = 8),
MEDIUM(weight = 16),
LARGE(weight = 32),
}
使用枚举的用法when
我们无法创建enum类的对象,因为它在内部被实现为抽象类。
我们也可以在这里保存数据:
enum class Size(val cost: Int) {
SMALL(cost = 8),
MEDIUM(cost = 16),
LARGE(cost = 32),
}
fun handleSize(coffeeSize: Size) {
when (coffeeSize) {
Size.SMALL -> {
println("Cost of small coffee: ${coffeeSize.cost} $")
}
Size.MEDIUM -> {
println("Cost of medium coffee: ${coffeeSize.cost} $")
}
Size.LARGE -> {
println("Cost of large coffee: ${coffeeSize.cost} $")
}
}
}
fun main() {
handleSize(Size.MEDIUM)
// Output
// Cost of medium coffee: 16 $
}
枚举可用于when:
when (coffeeSize) {
Size.SMALL -> {
println("Cost of small coffee: ${coffeeSize.cost} $")
}
Size.MEDIUM -> {
println("Cost of medium coffee: ${coffeeSize.cost} $")
}
}
val size = when (coffeeSize) {
Size.SMALL -> {
println("Cost of small coffee: ${coffeeSize.cost} $")
}
Size.MEDIUM -> {
println("Cost of medium coffee: ${coffeeSize.cost} $")
}
}
如果我们考虑内部枚举的所有常量when块作为expression,即使添加了else块,也不必添加它,它将被视为冗余。
枚举类有问题
它只能保存单一类型的状态(数据)。
enum class Network(val response : Boolean){
SUCCESS(true)
FAILURE(false)
}
上面的代码示例中有两个常量:成功和失败。如果一个网络调用成功,则传递true;如果失败,我们就判false。
但是如果我们想在网络调用失败时传输异常或消息对象呢?
enum class Network(val response : Boolean){
SUCCESS(true)
FAILURE("No Internet Connection") // error
}
因为我们要给string而不是布尔型作为响应,上面的代码示例将失败。
当我们确定将遇到可比较类型的状态和常量时,应该使用Enum类,因为Enum在这种情况下不灵活,只能维护与其自身相似的状态类型。
密封类
密封类允许您表示受约束的层次结构,其中对象只能是给定类型之一。
密封类在其中定义了一组子类。
密封类通过将类型限制为在编译时而不是在运行时匹配来确保类型安全。
为什么要使用密封类?
在某些方面,密封类与枚举类是相同的,但是正如我们前面提到的,枚举类只作为一个实例/状态存在,而枚举类的子类可能有多个实例,每个实例都有自己的状态。
每个子类都可以有自己的构造函数参数集,这在枚举状态下是不可能的。
如何使用密封类?
密封类的子类可以具有不同的构造函数参数集,我们使用sealed关键字和类名来定义密封类
sealed class NetworkState{
data class Success(val data : String) : NetworkState()
data class Error(val error : Throwable) : NetworkState()
object Loading : NetworkState()
}
密封类的用法when
密封类的子类可以具有不同的构造函数参数集
就像when,在枚举的情况下,when as语句不是穷尽的,但when as表达式在密封类的情况下也是穷尽的。了解什么是详尽的?
密封类也可以有所有的对象声明子类,当以这种方式使用时,它非常类似于枚举。
sealed class NetworkState{
data class Success(val data : String) : NetworkState()
data class Error(val error : Throwable) : NetworkState()
object Loading : NetworkState()
}
fun getNetworkState(networkState : NetworkState) : Any{
return when(networkState){
is NetworkState.Success -> {
networkState.data
}
is NetworkState.Error -> {
networkState.error
}
networkState.Loading -> {
"Loading..."
}
}
}
fun main(){
val ans = getNetworkState(NetworkState.Success(data = "Network Response"));
println(ans)
}
什么时候用什么?
当我们期望看到类似类型的状态(数据)和常量时,我们应该实现enum类。
在处理不同的状态(数据)时,就像NetworkResponse中的情况一样,我们应该使用密封类,因为它们允许我们对成功和错误实例使用不同的类型。