Clases selladas y enumeradas.Definiciones, diferencias y casos de uso | Por Neeraja Gandla | Abril de 2022

Definiciones, diferencias y casos de uso

Este artículo pretende explicar lo siguiente:
- Definición de enumeración
- La necesidad de la enumeración
- ¿Cómo y cuándo usar enumeraciones?
- Agregar parámetro de constructor a la enumeración
- Algunos métodos soportados por Enums
- propiedades específicas del elemento de la enumeración
- Uso de enumeraciones en bloques when
- Definición de una clase sellada
- Visibilidad del constructor admitida en clases selladas
- Demostrar casos de uso donde las clases selladas son mejores que las enumeraciones
Cuando tenemos que representar un conjunto constante de opciones posibles, una opción clásica es usar una enumeración. Si alguna vez ha usado diferentes constantes para representar varios estados de un proceso, es posible que se pregunte por qué usar enumeraciones en primer lugar. ¿No podemos simplemente usar diferentes constantes de cadena?
¿Por qué enumerar en primer lugar?
Aquí hay una gran respuesta en StackOverflow que explica el concepto:
https://stackoverflow.com/a/11575421/5742365
¿Cómo y cuándo usar enumeraciones?
Supongamos que queremos representar los días de la semana, podemos definir una enumeración de la siguiente manera:
enum class Weekday
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Parámetros de constructor en enumeraciones
También podemos agregar parámetros de constructor a las clases de enumeración. Supongamos que necesitamos una clase de enumeración para representar un conjunto de colores. Teniendo en cuenta sus códigos de color RGB, cada color puede tener su propia proporción de rojo, verde y azul. Eche un vistazo al siguiente ejemplo:
enum class Color(open val red: Int, val green: Int, open val blue: Int) {
RED(255, 0, 0),
GREEN(0, 255, 0),
BLUE(0, 0, 255);
}
Los métodos en las clases de enumeración también pueden ser anulados por cualquier constante de enumeración. En el fragmento de código a continuación, anulamos el método getRed() en el caso RED.
enum class Color(open val red: Int, val green: Int, open var blue: Int) {
RED(255, 0, 0) {
override var red = 200
override var blue = 55val test: String
get() = "Test"
},
GREEN(0, 255, 0),
BLUE(0, 0, 255);
}
También agregué un test
Propiedades en ROJO. Pero no podemos acceder a esa propiedad. Si intentamos hacer esto, la propiedad no se reconoce. Esto se debe a que, en el caso de las enumeraciones, solo podemos anular métodos o propiedades de la clase. No podemos definir nuevos métodos o propiedades y usarlos.
Cada caso de enumeración utiliza el mismo conjunto de parámetros de constructor definidos en el constructor de clase. No podemos instanciar clases de enumeración como clases normales. P.ej,
val color = Color()
No funciona con las clases de enumeración anteriores. Solo podemos usar casos en clases de enumeración y usarlos.
Algunos métodos soportados por la clase Enum
La clase de enumeración anterior se puede utilizar como se muestra en el siguiente ejemplo:
fun main() {
val color = Color.BLUE
println(color.name)
println(color.blue)println()
println(Color.RED.red)
println(Color.RED.blue)
println(Color.RED.green)println()
val colors = Color.values()
for(c in colors)
Output:
println("${c.ordinal} : ${c.name}")
}
BLUE
255200
55
0 : RED
1 : GREEN
2 : BLUE
Juega con el ejemplo anterior para comprender mejor las enumeraciones.
Las enumeraciones son específicas del proyecto.
Los valores que puede contener una enumeración siempre son específicos del proyecto. P.ej,
val red = Color.RED
val red2 = Color.RED
println(red == red2) //prints truered2.blue = 25
println(red.blue) //prints 25
Uso de enumeraciones en bloques when
La enumeración se puede utilizar para when
en Kotlin. when
Se puede utilizar como declaración o expresión.
when(color) {
Color.RED -> println("The color is red: ${color.red}, ${color.green}, ${color.blue}")
Color.GREEN -> println("The color is green: ${color.red}, ${color.green}, ${color.blue}")
}
En el fragmento de código anterior, when se usa como declaración. Por lo tanto, ignora el hecho de que nos perdimos el bloque Color.BLUE o else.
val answer = when(color) {
Color.RED -> "The color is red: ${color.red}, ${color.green}, ${color.blue}"
Color.GREEN -> "The color is green: ${color.red}, ${color.green}, ${color.blue}"
}
En el fragmento de código anterior, when se usa como una expresión.Esta vez muestra un error del compilador porque a la variable se le debe asignar un valor answer
. when
Las expresiones deben ser exhaustivas.Así que tenemos que agregar un bloque else, o Color.BLUE
.
val answer = when(color) {
Color.RED -> "The color is red: ${color.red}, ${color.green}, ${color.blue}"
Color.GREEN -> "The color is green: ${color.red}, ${color.green}, ${color.blue}"
Color.BLUE -> "The color is blue: ${color.red}, ${color.green}, ${color.blue}"
}
println(answer)
Si cubrimos todos los casos de la clase enum en la expresión when, no necesitamos agregar el bloque else. Incluso si lo agregamos, el compilador lo considerará redundante. También nos advierte lo mismo – when
muy detallado, por lo que else
Es redundante aquí.
Las clases selladas en Kotlin son otro nuevo concepto que no tenemos en Java y abre otro nuevo mundo de posibilidades.
Las clases selladas te permiten Representa una jerarquía restringida donde los objetos solo pueden ser de uno de los tipos dados.
Es decir, tenemos una clase con un cierto número de subclases.En cierto sentido, una clase sellada es similar a enum
Clases: el conjunto de valores para los tipos de enumeración también está restringido, pero cada constante de enumeración solo se usa como un única instanciamientras que una subclase de una clase sellada puede tener muchos Instancias, cada una con su propio estado.
Por ejemplo, considere la API de una biblioteca. Puede contener clases de error para permitir que los usuarios de la biblioteca manejen los errores que pueda generar. Si la jerarquía de dichas clases de error incluye interfaces o clases abstractas que son visibles en la API pública, nada impide que se implementen o amplíen en el código del cliente. Sin embargo, la biblioteca desconoce los errores declarados fuera de ella, por lo que no puede tratarlos de forma coherente con sus propias clases. Usando una jerarquía de clase de error sellada, los autores de la biblioteca pueden asegurarse de que conocen todos los tipos de error posibles y que no aparecerá ningún otro tipo de error más adelante.
Cada subclase puede tener su propio conjunto de parámetros de constructor, lo que no es posible en el caso de las enumeraciones.
esta diferencia permitirá que los objetos en clases selladas mantengan el estadoEsto nos dará algunas ventajas, que veremos a continuación.
Sellar una clase restringe su jerarquía de clases.Cuando marcas una clase como sealed
, solo puede subclasificarlo desde la ubicación especificada. Todas las subclases directas de una clase sellada se conocen en tiempo de compilación. Esto le permite restringir las subclases para una evaluación exhaustiva.
sealed class Error
sealed class IOError(): Error // extended only in same package and module
class FileReadError(val file: File): IOError()
class DatabaseError(val source: DataSource): IOError()
object RuntimeError : Error
Estas restricciones no se aplican a las subclases indirectas. Si una subclase directa de una clase sellada no está marcada como sellada, se puede extender de cualquier manera que su modificador permita:
sealed interface Error // has implementations only in same package and modulesealed class IOError(): Error // extended only in same package and module
open class CustomError(): Error // can be extended wherever it's visible
Visibilidad de constructores en clases selladas
El constructor de una clase sellada puede tener una de dos visibilidades: protected
(predeterminado) o private
:
sealed class IOError {
constructor() { /*...*/ } // protected by default
private constructor(description: String): this() { /*...*/ } // private is OK
// public constructor(code: Int): this() {} // Error: public and internal are not allowed
}
Caso de uso que muestra las ventajas de las clases selladas sobre las enumeraciones
Las subclases de clases selladas pueden tener diferentes conjuntos de parámetros de constructor. P.ej,
sealed class Responsefun handle(response: Response
class Success(val value: R): Response ()
class Failure(val error: Throwable): Response() ) {
val text = when (response) {
is Success -> "Success, data are: " + response.valuei
is Failure -> "Error"
}
print(text)
}
me gusta when
En el caso de la enumeración, when
No es una declaración exhaustiva, pero when
En el caso de clases selladas, las expresiones también son exhaustivas.
Una clase sellada también puede tener todas las declaraciones de objetos subclasificadas, y cuando se usa así, es muy similar a una enumeración:
sealed class PaymentOption {
object Cash
object Card
object Transfer
}
Aunque No usamos clases selladas de esta manera. Porque las enumeraciones son más adecuadas para esta situación.
La ventaja de las clases selladas sobre las enumeraciones es que las subclases pueden contener datos específicos de la instancia. Por ejemplo, cuando notificamos a otra parte de la aplicación sobre la opción de pago seleccionada, podemos pasar tanto el tipo de pago seleccionado como los datos específicos del pago necesarios para un procesamiento posterior.
import java.math.BigDecimal sealed class Paymentdata class CashPayment(val amount: BigDecimal, val pointId: Int): Payment()data class CardPayment(val amount: BigDecimal, val orderId: Int): Payment()data class BankTransfer(val amount: BigDecimal, val orderId: Int): Payment() fun process(payment: Payment) {
when (payment) {
is CashPayment -> {
showPaymentInfo(payment.amount, payment.pointId)
}
is CardPayment -> {
moveToCardPaiment(payment.amount, payment.orderId)
}
is BankTransfer -> {
val bankTransferRepo = BankTransferRepo()
val transferDetails = bankTransferRepo.getDetails()
displayTransferInfo(payment.amount, transferDetails)
bankTransferRepo.setUpPaymentWathcher(payment.orderId,
payment.amount, transferDetails)
}
}
}
Un buen ejemplo de una clase sellada es representar varios eventos o mensajes, ya que ambos sabemos cuál es ese evento y cada evento puede contener datos.
Una clase de enumeración representa un conjunto concreto de valores, mientras que una clase sellada representa un conjunto concreto de clases. Dado que estas clases pueden ser declaraciones de objetos, podemos usar clases selladas en lugar de enumeraciones hasta cierto punto, pero no al revés.
La ventaja de las clases de enumeración es que se pueden serializar y deserializar de forma inmediata.ellos tienen una manera values()
y valueOf
También podemos obtener el valor de enumeración usando el tipo enumValues
y enumValueOf
funciones Enumerando números ordinales, podemos mantener datos constantes por elemento. Son excelentes para representar un conjunto constante de valores posibles.
La ventaja de las clases selladas es que pueden contener datos específicos de la instancia. Cada elemento puede ser una clase o un objeto (creado usando una declaración de objeto). Representan un conjunto de clases alternativas (tipos de suma, coproductos). Son útiles para definir alternativas, Result
Es decir Success
o Failure
, Tree
Es decir Leaf
o Node
, o un valor JSON, que puede ser una lista, un objeto, una cadena, un booleano, un entero o un valor nulo. También son excelentes para definir un conjunto de posibles eventos o mensajes.
Referirse a
Algunos ejemplos y explicaciones en esta publicación de blog están tomados de
y
Espero que este artículo lo haya ayudado a comprender mejor los diferentes casos de uso de enumeraciones y clases selladas.