Background
I'm developing a Spring Boot application and I'm using Kotlin, IntelliJ and Gradle (Groovy). I have some enum class in my code and I need to persist them (with JPA). I used a simple global converter.
// Sample Enum
enum class Policy {
PUBLIC,
INVITE_ONLY
}
// Sample Converter
#Converter(autoApply = true)
class PolicyConverter : AttributeConverter<Policy, String> {
override fun convertToDatabaseColumn(attribute: Policy): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): Policy {
return Policy.valueOf(dbData.toUpperCase())
}
}
Problem
Since I have 5-6 enums and I hate duplicated code, I thought about a generic converter that should do the work for every given enum. I tried to code something, but nothing worked. This is what I was thinking about:
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
override fun convertToDatabaseColumn(attribute: E): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): E {
return E.valueOf(dbData.toUpperCase())
}
}
In this way I can only extend from one abstract class every enum converter, like so:
#Converter(autoApply = true)
class PolicyConverter : EnumConverter<Policy>() {}
Problem with this code is that I have two errors:
E is red because: Type parameter 'E' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
valueOf is red because: unresolved reference (there are like 150+ types of .valueOf).
As suggested from this I tried to use following function:
private inline fun <reified E : Enum<E>> getValue(string: String): E {
return enumValueOf(string.toUpperCase())
}
But when called from the .convertToEntityAttribute, the result is that "Cannot use 'E' as reified type parameter. Use a class instead."
Question
So the question is simple: how can I implement an easy and fast way to make one converter for all my enums, that all follows the same principle? I just need a return E.valueOf(<value>) function, but it's not present.
A simply workaround of this problem is to define an abstract method that every class will implement and it will return the correct type, given a string.
// Inside EnumConverter, the Generic Class
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
abstract fun getValueFromString(string: String) : E
override fun convertToEntityAttribute(dbData: String): E {
return getValueFromString(dbData)
}
[...]
}
// Inside Policy Enum, implementing
class PolicyConverter : EnumConverter<Policy>() {
override fun getValueFromString(string: String): Policy {
return Policy.valueOf(string.toUpperCase())
}
}
But it's a workaround that I really dislike.
I'd like to create a color object based on an Int. I can achieve the same result using sealed class and enum and was wondering if one is better than the other.
Using sealed class:
sealed class SealedColor(val value: Int) {
class Red : SealedColor(0)
class Green : SealedColor(1)
class Blue : SealedColor(2)
companion object {
val map = hashMapOf(
0 to Red(),
1 to Green(),
2 to Blue()
)
}
}
val sealedColor: SealedColor = SealedColor.map[0]!!
when (sealedColor) {
is SealedColor.Red -> print("Red value ${sealedColor.value}")
is SealedColor.Green -> print("Green value ${sealedColor.value}")
is SealedColor.Blue -> print("Blue value ${sealedColor.value}")
}
Using enum:
enum class EnumColor(val value: Int) {
Red(0),
Green(1),
Blue(2);
companion object {
fun valueOf(value: Int): EnumColor {
return EnumColor
.values()
.firstOrNull { it.value == value }
?: throw NotFoundException("Could not find EnumColor with value: $value")
}
}
}
val enumColor: EnumColor = EnumColor.valueOf(0)
when (enumColor) {
EnumColor.Red -> print("Red value ${enumColor.value}")
EnumColor.Green -> print("Green value ${enumColor.value}")
EnumColor.Blue -> print("Blue value ${enumColor.value}")
}
Are they equivalent in term of performance? Is there a better kotlin way to achieve the same result?
Let's discuss the difference between enums and sealed classes over various aspects with contrasting examples. This will help you choose one over the other depending on your use case.
Properties
Enum
In enum classes, each enum value cannot have its own unique property. You are forced to have the same property for each enum value:
enum class DeliveryStatus(val trackingId: String?) {
PREPARING(null),
DISPATCHED("27211"),
DELIVERED("27211"),
}
Here we need the trackingId only for the DISPATCHED and DELIVERED, the PREPARING is forced to have a null value.
Sealed Class
In case of sealed classes, we can have different properties for each subtype:
sealed class DeliveryStatus
class Preparing() : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val trackingId: String, val receiversName: String) : DeliveryStatus()
Here we have different properties for each subtype. Preparing doesn't need properties for our use case, so we have the flexibility to not specify any property unlike forced null values in enums. Dispatched has one property while the Delivered has two properties.
Considering the example Color(val value: Int) in the question, you have a common value: Int property for all constants and since you don't need different properties for different constants, you should use enums in this case.
Functions
Enum
Enums can have abstract functions as well as regular functions. But like properties, each enum value also has to have the same function:
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()
}
In this example, we have an abstract function cancelOrder() that we have to override in each enum value. That means, we can't have different functions for different enum values.
Usage:
class DeliveryManager {
fun cancelOrder(status: DeliveryStatus) {
status.cancelOrder()
}
}
Sealed Class
In sealed classes we can have different functions for different subtypes:
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")
}
Here we have different functions: cancelOrder() for Preparing, rejectDelivery() for Dispatched and returnItem() for Delivered. This makes the intent clearer and makes the code more readable, also we have the option of not having the function, in case we don't want to.
Usage:
class DeliveryManager {
fun cancelOrder(status: DeliveryStatus) = when(status) {
is Preparing -> status.cancelOrder()
is Dispatched -> status.rejectDelivery()
is Delivered -> status.returnItem()
}
}
If we want a common function for all subtypes like in the enum example, we can have that in sealed class by defining it in the sealed class itself and then overriding it in the subtypes:
sealed class DeliveryStatus {
abstract fun cancelOrder()
}
The advantage of having a common function for all types is that we don't have to type check using the is operator. We can simply use polymorphism as shown in the DeliveryManager class of enum example.
Inheritance
Enum
Since enum values are objects, they cannot be extended:
class LocallyDispatched : DeliveryStatus.DISPATCHED { } // Error
The enum class is implicitly final, so it cannot be extended by other classes:
class FoodDeliveryStatus : DeliveryStatus() { } // Error
Enum classes cannot extend other classes, they can only extend interfaces:
open class OrderStatus { }
interface Cancellable { }
enum class DeliveryStatus : OrderStatus() { } // Error
enum class DeliveryStatus : Cancellable { } // OK
Sealed Class
Since sealed class' subtypes are types, they can be extended:
class LocallyDispatched : Dispatched() { } // OK
The sealed class itself can be extended, of course!:
class PaymentReceived : DeliveryStatus() // OK
Sealed classes can extend other classes as well as interfaces:
open class OrderStatus { }
interface Cancellable { }
sealed class DeliveryStatus : OrderStatus() { } // OK
sealed class DeliveryStatus : Cancellable { } // OK
Number of Instances
Enum
Since enum values are objects and not types, we cannot create multiple instances of them:
enum class DeliveryStatus(val trackingId: String?) {
PREPARING(null),
DISPATCHED("27211"),
DELIVERED("27211"),
}
In this example, DISPATCHED is an object and not a type, so it can exist only as a single instance, we can't create more instances from it:
// Single instance
val dispatched1 = DeliveryStatus.DISPATCHED // OK
// Another instance
val dispatched2 = DeliveryStatus.DISPATCHED("45234") // Error
Sealed Class
Subtypes of sealed classes are types, so we can create multiple instances of these types. We can also make a type to have only a single instance using an object declaration:
sealed class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
data class Delivered(val receiversName: String) : DeliveryStatus()
In this example, we can create multiple instances of Dispatched and Delivered. Notice that we have utilised the ability of the subtypes of the sealed class to be as a singleton object, a regular class or a data class. Preparing can can have only a single object, just like an enum value:
// Multiple Instances
val dispatched1 = Dispatched("27211") // OK
val dispatched2 = Dispatched("45234") // OK
// Single Instance
val preparing1 = Preparing // OK
val preparing2 = Preparing() // Error
Notice also that in the code above, each instance of Dispatched can have a different value for the trackingId property.
Serializable and Comparable
Enum
Every enum class in Kotlin is implicitly extended by the abstract class java.lang.Enum. So, all the enum values automatically have the implementations for equals(), toString(), hashCode(), Serializable and Comparable. We don't have to define them.
Sealed Class
For sealed classes we need to define them manually or use data class for the automatic equals(), toString() and hashcode() and then implement Serializable and Comparable manually.
Performance
Enum
Enums don't get garbage collected, they stay in memory for the lifespan of your app. This can be an upside or a downside.
Garbage collection process is expensive. The same is true for object creation, we don't want to create the same objects again and again. So, with enums, you save the cost of garbage collection as well as object creation. This is the upside.
The downside is that the enums stay in memory even when they are not in use, this can keep the memory occupied all the time.
You don't need to worry about all this, if you have 100 to 200 enums in your app. But when you have more than that, you have a decision to make whether you should go for enums depending on the facts such as the number of enums, whether they will be in use all the time and the amount of memory allocated to your JVM.
The comparison of enum values is faster in the when expression because under the hood, it uses tableswitch to compare the objects. So, for the example given in the question, enums should be preferred as they will be faster in this case.
In Android, when the optimization is enabled, the Proguard converts the enums that don't have functions and properties to integers, so you get the type-safety of the enums at compile-time and the performance of the ints at runtime!
Sealed Class
Sealed classes are just regular classes with the only exception that they need to be extended in the same package and the same compilation unit. So, their performance is equivalent to regular classes.
Objects of the subtypes of the sealed classes get garbage collected like the objects of regular classes. So, you have to bear the cost of garbage collection as well as object creation.
When you have the low memory constraints, you may consider using sealed classes instead of enums, if you need thousands of objects. Because the garbage collector can free up the memory when the objects are not in use.
If you use object declaration for extending the sealed class, the objects act as singletons and they won't be garbage collected, just like enums.
The comparison of sealed class' types is slower in when expression because under the hood it uses instanceof to compare the types. The speed difference between enums and sealed classes, in this case, is very little though. It matters only when you are comparing thousands of constants in a loop.
That's it! Hopefully, this will make it easier for you to choose one over the other.
A sealed class is "an extension of enum classes". They can exist in multiple instances that contain state while each enum constant exists only as a single instance.
Since, in your example, you don't need the values to be instantiated multiple times and they don't provide special behavior, enums should just be right for the use case.
Also, see the docs.
My solution is like this and it will give you list of all subclass instance val list = BaseClass::class.sealedSubclasses.map{it.createInstance()}
Consider a nested enum class, which is passed as argument to a member function of that class.
class VerboseClassName {
public:
enum class Unit {KG, TON};
void foo(Unit unit) { /*...*/ }
};
Using this type of construction always feels kinda awkward to me, as the class name has to be qualified even from within the method call.
void bar() {
VerboseClassName a;
a.foo(VerboseClassName::Unit::KG);
}
Would it make sense to consider the class scope available in calls to the classes members? Is there a technical reason why this would lead to trouble?
I am a little confused about these types.
Especially XXXDelegate_Extention classes. If I want to implement an optional protocol method, will this XXXDelegate_Extention be useful? Or I always need to subclass the XXXDelegate class?
Thanks!
Delegates on iOS can have optional methods. Since the interface IXXDelegate only declares the non-optional functions, XXXDelegate only implements the non-optional functions. The XXXDelegate_Extention adds the optional functionsto the delegate (interface). So you can either inherit from XXXDelegate or implement IXXXDelegate completely on your own. I'd prefer inheriting, unless you really need to have a totally custom implementation.
Extension methods in C# are methods, that can be called like instance methods, but are not declared within the type of this instance.
Example on Extension methods
public class MyClass
{
public int Foo(int a)
{
return a + 1;
}
}
public static class MyClassExtension
{
public static int Bar(this MyClass my, int a)
{
return my.Foo(a+1);
}
}
var x = new MyClass();
x.Foo(2); // returns 3
x.Bar(2); // returns 4
More info: https://msdn.microsoft.com/en-us//library/bb383977.aspx
Good day, I have a fairly simple question to experienced C# programmers. Basically, I would like to have an abstract base class that contains a function that relies on the values of child classes. I have tried code similar to the following, but the compiler complains that SomeVariable is null when SomeFunction() attempts to use it.
Base class:
public abstract class BaseClass
{
protected virtual SomeType SomeVariable;
public BaseClass()
{
this.SomeFunction();
}
protected void SomeFunction()
{
//DO SOMETHING WITH SomeVariable
}
}
A child class:
public class ChildClass:BaseClass
{
protected override SomeType SomeVariable=SomeValue;
}
Now I would expect that when I do:
ChildClass CC=new ChildClass();
A new instance of ChildClass should be made and CC would run its inherited SomeFunction using SomeValue. However, this is not what happens. The compiler complains that SomeVariable is null in BaseClass. Is what I want to do even possible in C#? I have used other managed languages that allow me to do such things, so I certain I am just making a simple mistake here.
Any help is greatly appreciated, thank you.
You got it almost right, but you need to use properties instead of variables:
public abstract class BaseClass {
protected SomeType SomeProperty {get; set}
public BaseClass() {
// You cannot call this.SomeFunction() here: the property is not initialized yet
}
protected void SomeFunction() {
//DO SOMETHING WITH SomeProperty
}
}
public class ChildClass:BaseClass {
public ChildClass() {
SomeProperty=SomeValue;
}
}
You cannot use FomeFunction in the constructor because SomeProperty has not been initialized by the derived class. Outside of constructor it's fine, though. In general, accessing virtual members in the constructor should be considered suspicious.
If you must pass values from derived classes to base class constructor, it's best to do it explicitly through parameters of a protected constructor.