Ya no tienes que indagar más por otros sitios ya que estás al lugar exacto, contamos con la solución que buscas pero sin problema.
Solución:
A sealed
la clase es “una extensión de las clases de enumeración”. Pueden existir en Múltiples instancias que contienen estado, mientras que cada constante de enumeración existe solo como una única instancia.
Dado que, en su ejemplo, no es necesario que se creen instancias de los valores varias veces y no proporcionan un comportamiento especial, enumeraciones debería ser adecuado para el caso de uso.
Además, consulte los documentos.
Analicemos la diferencia entre enumeraciones y clases selladas en varios aspectos con ejemplos contrastantes. Esto lo ayudará a elegir uno sobre el otro según su caso de uso.
Propiedades
Enum
En las clases de enumeración, cada valor de enumeración no puede tener su propia propiedad única. Está obligado a tener la misma propiedad para cada valor de enumeración:
enum class DeliveryStatus(val trackingId: String?)
PREPARING(null),
DISPATCHED("27211"),
DELIVERED("27211"),
Aquí necesitamos el trackingId
solo para el DISPATCHED
y DELIVERED
, los PREPARING
se ve obligado a tener un null
valor.
Clase sellada
En el caso de clases selladas, podemos tener diferentes propiedades para cada subtipo:
sealed class DeliveryStatus
class Preparing() : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val trackingId: String, val receiversName: String) : DeliveryStatus()
Aquí tenemos diferentes propiedades para cada subtipo. Preparing
no necesita propiedades para nuestro caso de uso, por lo que tenemos la flexibilidad de no especificar ninguna propiedad a diferencia de las forzadas null
valores en enumeraciones. Dispatched
tiene una propiedad mientras que el Delivered
tiene dos propiedades.
Considerando el ejemplo Color(val value: Int)
en la pregunta, tienes un común value: Int
propiedad para todas las constantes y dado que no necesita propiedades diferentes para constantes diferentes, debe usar enumeraciones en este caso.
Funciones
Enum
Las enumeraciones pueden tener funciones abstractas así como funciones regulares. Pero al igual que las propiedades, cada valor de enumeración también debe tener la misma función:
enum class DeliveryStatus
PREPARING
override fun cancelOrder() = println("Cancelled successfully")
,
DISPATCHED
override fun cancelOrder() = println("Delivery rejected")
,
DELIVERED
override fun cancelOrder() = println("Return initiated")
;
abstract fun cancelOrder()
En este ejemplo, tenemos un abstract
función cancelOrder()
que tenemos que override
en cada valor de enumeración. Eso significa que no podemos tener diferentes funciones para diferentes valores de enumeración.
Uso:
class DeliveryManager
fun cancelOrder(status: DeliveryStatus)
status.cancelOrder()
Clase sellada
En clases selladas podemos tener diferentes funciones para diferentes subtipos:
sealed class DeliveryStatus
class Preparing : DeliveryStatus()
fun cancelOrder() = println("Cancelled successfully")
class Dispatched : DeliveryStatus()
fun rejectDelivery() = println("Delivery rejected")
class Delivered : DeliveryStatus()
fun returnItem() = println("Return initiated")
Aquí tenemos diferentes funciones: cancelOrder()
por Preparing
, rejectDelivery()
por Dispatched
y returnItem()
por Delivered
. Esto aclara el intent y hace que el código sea más legible, además tenemos la opción de no tener la función, en caso de que no queramos.
Uso:
class DeliveryManager
fun cancelOrder(status: DeliveryStatus) = when(status)
is Preparing -> status.cancelOrder()
is Dispatched -> status.rejectDelivery()
is Delivered -> status.returnItem()
Si queremos una función común para todos los subtipos como en el ejemplo de enumeración, podemos tenerla en la clase sellada definiéndola en la propia clase sellada y luego anulándola en los subtipos:
sealed class DeliveryStatus
abstract fun cancelOrder()
La ventaja de tener una función común para todos los tipos es que no tenemos que escribir check usando el is
operador. Simplemente podemos usar polimorfismo como se muestra en la DeliveryManager
ejemplo de clase de enumeración.
Herencia
Enum
Ya que enum
los valores son objetos, no se pueden ampliar:
class LocallyDispatched : DeliveryStatus.DISPATCHED // Error
los enum class
es implícitamente final
, por lo que no puede ser extendido por otras clases:
class FoodDeliveryStatus : DeliveryStatus() // Error
Las clases de enumeración no pueden extender otras clases, solo pueden extender interfaces:
open class OrderStatus
interface Cancellable
enum class DeliveryStatus : OrderStatus() // Error
enum class DeliveryStatus : Cancellable // OK
Clase sellada
Dado que los subtipos de clase sellada son tipos, se pueden extender:
class LocallyDispatched : Dispatched() // OK
¡La clase sellada en sí se puede extender, por supuesto !:
class PaymentReceived : DeliveryStatus() // OK
Las clases selladas pueden ampliar otras clases, así como interfaces:
open class OrderStatus
interface Cancellable
sealed class DeliveryStatus : OrderStatus() // OK
sealed class DeliveryStatus : Cancellable // OK
Numero de instancias
Enum
Dado que los valores de enumeración son objetos y no tipos, no podemos crear varias instancias de ellos:
enum class DeliveryStatus(val trackingId: String?)
PREPARING(null),
DISPATCHED("27211"),
DELIVERED("27211"),
En este ejemplo, DISPATCHED
es un objeto y no un tipo, por lo que solo puede existir como una única instancia, no podemos crear más instancias a partir de él:
// Single instance
val dispatched1 = DeliveryStatus.DISPATCHED // OK
// Another instance
val dispatched2 = DeliveryStatus.DISPATCHED("45234") // Error
Clase sellada
Los subtipos de clases selladas son tipos, por lo que podemos crear varias instancias de estos tipos. También podemos hacer que un tipo tenga solo una instancia usando un object
declaración:
sealed class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
data class Delivered(val receiversName: String) : DeliveryStatus()
En este ejemplo, podemos crear varias instancias de Dispatched
y Delivered
. Observe que hemos utilizado la capacidad de los subtipos de la clase sellada para ser un singleton object
, un habitual class
o un data class
. Preparing
puede tener solo una object
, al igual que un valor de enumeración:
// Multiple Instances
val dispatched1 = Dispatched("27211") // OK
val dispatched2 = Dispatched("45234") // OK
// Single Instance
val preparing1 = Preparing // OK
val preparing2 = Preparing() // Error
Observe también que en el código anterior, cada instancia de Dispatched
puede tener un valor diferente para el trackingId
propiedad.
Serializable y comparable
Enum
Cada enum class
en Kotlin se extiende implícitamente por la clase abstracta java.lang.Enum
. Entonces, todos los valores de enumeración tienen automáticamente las implementaciones para equals()
, toString()
, hashCode()
, Serializable
y Comparable
. No tenemos que definirlos.
Clase sellada
Para las clases selladas, debemos definirlas manualmente o usar data class
para el automatico equals()
, toString()
y hashcode()
y luego implementar Serializable
y Comparable
a mano.
Rendimiento
Enum
Las enumeraciones no se recolectan basura, permanecen en la memoria durante la vida útil de su aplicación. Esto puede ser positivo o negativo.
El proceso de recolección de basura es costoso. Lo mismo es true para la creación de objetos, no queremos crear los mismos objetos una y otra vez. Entonces, con las enumeraciones, ahorra el costo de la recolección de basura y la creación de objetos. Este es el lado positivo.
La desventaja es que las enumeraciones permanecen en la memoria incluso cuando no están en uso, esto puede mantener la memoria ocupada todo el tiempo.
No necesita preocuparse por todo esto, si tiene de 100 a 200 enumeraciones en su aplicación. Pero cuando tiene más que eso, tiene que tomar una decisión sobre si debe buscar enumeraciones en función de los hechos, como la cantidad de enumeraciones, si estarán en uso todo el tiempo y la cantidad de memoria asignada a su JVM.
La comparación de valores de enumeración es más rápida en el when
expresión porque debajo del capó, usa tableswitch
para comparar los objetos. Entonces, para el ejemplo dado en la pregunta, se deben preferir las enumeraciones, ya que serán más rápidas en este caso.
En Android, cuando la optimización está habilitada, Proguard convierte las enumeraciones que no tienen funciones y propiedades en números enteros, por lo que obtiene la seguridad de tipos de las enumeraciones en tiempo de compilación y el rendimiento de las entradas en tiempo de ejecución.
Clase sellada
Las clases selladas son solo clases regulares con la única excepción de que deben extenderse en el mismo paquete y en la misma unidad de compilación. Entonces, su desempeño es equivalente a las clases regulares.
Los objetos de los subtipos de las clases selladas se recolectan como basura como los objetos de las clases regulares. Por lo tanto, debe asumir el costo de la recolección de basura y la creación de objetos.
Cuando tiene limitaciones de memoria baja, puede considerar el uso de clases selladas en lugar de enumeraciones, si necesita miles de objetos. Porque el recolector de basura puede liberar memoria cuando los objetos no están en uso.
Si utiliza object
declaración para extender la clase sellada, los objetos actúan como singletons y no serán recolectados como basura, como enums.
La comparación de tipos de clases selladas es más lenta en when
expresión porque debajo del capó usa instanceof
para comparar los tipos. Sin embargo, la diferencia de velocidad entre enumeraciones y clases selladas, en este caso, es muy pequeña. Solo importa cuando se comparan miles de constantes en un bucle.
¡Eso es todo! Con suerte, esto le facilitará la elección de uno sobre el otro.
Tienes la opción de añadir valor a nuestra información asistiendo con tu veteranía en los informes.