I´m working on a legacy spring boot project that makes a strong reuse of a DTO class in a generic controller and in multiple services:
#PostMapping
controller.input(#RequestBody MyTypeDto type) { ... }
service.resolve(MyTypeDto type) { ... }
processor.send(MyTypeDto type) { ... }
I want to start decoupling it by creating another endpoint and making MyTypeDto an abstract class.
My biggest concern under all is the compatility with jackson.
public abstract class MyTypeDto { ... }
public class AnotherTypeDto extends MyTypeDto { ... }
public class AndAnotherTypeDto extends MyTypeDto { ... }
Is it considered a good practice?
As it is implied on your question, you controller endpoint is generic, it takes the input, creates the type, pass it to service based on subtype. Otherwise, you will end up many endpoints which all doing is creating the subtype and pass it to service.
If Jackson is your concern, Jackson has mechanism for subtypes. Please note you have to send one additional field which act as the discriminator (in this example, it is called type to decide which sub type to create.
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes({#JsonSubTypes.Type(value = FirstSubDto.class, name = "First"),
#JsonSubTypes.Type(value = SecondSubDto.class, name = "Second")})
public abstract class MyTypeDto {
..
}
Is this a right way to show the UML class diagram for the below code
public class Test {
Map<String, Map<String,Double>> bestOutput;
private Obj[] testMethod(Weight[][] input) {
}
}
I have a Test class and I also have Obj class and Weight class.
Is this class diagram correct for the above mentioned code snippets?
Thanks in advance
Is this class diagram correct for the above mentioned code snippets?
yes this is correct, and fortunately allowed by the tools
I profit of that question to also show the representation of the parameterized classes and the binding classes, if I use your example that can be :
even if in C++ we do for instance typedef Map<String, Double> String2Double; rather than class String2Double : public Map<String, Double> { ... }; (in BoUML to stereotype the class <<typedef>> produces that definition by default)
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()}
Kotlin already have number of "static" methods for enum class, like values and valueOf
For example I have enum
public enum class CircleType {
FIRST
SECOND
THIRD
}
How can I add static method such as random(): CircleType? Extension functions seems not for this case.
Just like with any other class, you can define a class object in an enum class:
enum class CircleType {
FIRST,
SECOND,
THIRD;
companion object {
fun random(): CircleType = FIRST // http://dilbert.com/strip/2001-10-25
}
}
Then you'll be able to call this function as CircleType.random().
EDIT: Note the commas between the enum constant entries, and the closing semicolon before the companion object. Both are now mandatory.
Here my class name is MyComparator2 but how can I use it here as a methods or object( am not sure ) MyComparator2() in another class name ComparaterDemo to define customized sorting . Can any one help to to make it clear regarding how one class can be used on another ??Thanks in advance
import java.util.Comparator;
import java.util.TreeSet;
public class ComparaterDemo {
public static void main(String[] args) {
TreeSet<Integer> treeSetCOllection = new TreeSet<Integer>(new MyComparator2());
treeSetCOllection.add(900);
treeSetCOllection.add(10);
treeSetCOllection.add(40);
treeSetCOllection.add(100);
treeSetCOllection.add(350);
System.out.println(treeSetCOllection);
}
}
class MyComparator2 implements Comparator{
public int compare(Object obj1, Object obj2){
Integer objectNumber1 = (Integer)obj1;
Integer objectNumber2 = (Integer)obj2;
if(objectNumber1<objectNumber2) return 1;
else if (objectNumber1>objectNumber2) return -1;
else return 0;
}
}
What's happening here is that you are constructing the TreeSet with the specified Comparator. How this works is, for each element added using the add() method, it will automatically sort the set. See here,
In order to make your class visible to other classes you need to specify its visibility. The three types of visibility in Java (I assume that's what you're using) are:
public
private
protected
They are defined as follows:
Public means that that class is visible to all classes regardless of where the file is located.
Private classes are classes inside of classes. For example, if MyComparator2 was defined inside of the ComparaterDemo class then ComparaterDemo could see it, but no other classes could.
Protected classes function the same as private classes except subclasses can also access them. So if MyComparator2 was defined inside of ComparaterDemo and SubComparaterDemo subclassed ComparaterDemo then SubComparaterDemo could access MyComparator2. In this example, no other classes except for ComparaterDemo and its subclasses could see it.
In your question, MyComparator2 is set to protected by default. If you change the line: "class MyComparator2 implements Comparator" to "public class MyComparator2 implements Comparator" you should have no more problems.
I hope this answers your question