-
Notifications
You must be signed in to change notification settings - Fork 23
Kotlin: Class vs DataClass
Differences |
---|
Choosing b/w Data & Normal Classes |
Key differences b/w normal & data class |
In Data class - Shallow Copy vs Deep Copy |
Data class, Why is it thread safe |
-
Use Data Classes: When you have a class primarily meant to hold data, and you want to benefit from the automatically generated methods. Data classes are particularly useful for model classes, DTOs (Data Transfer Objects), and similar scenarios.
-
Use Normal Classes: When you need more control over the behavior of your class or when you require mutable properties.
In Android development, both data classes and normal classes have their places, and the choice depends on the specific requirements of your application.
Differences |
---|
Default Methods |
Comparing two instances |
Component Functions |
Copy Method |
No-arg Constructor |
They automatically generate some commonly used methods like toString()
, equals()
, hashCode()
, and copy()
for you, whereas in a normal class, you would need to manually implement these methods.
You would need to implement these methods manually.
Comparing two instances based on the data it holds is simple
data class Person(val name: String, val age: Int)
val person1 = Person("John", 25)
val person2 = Person("John", 25)
println(person1 == person2) // true (equals() is automatically generated)
Here we need to implement our own custom comparator
or using equals
to achieve the same
Using equals
class PersonNormalClass(var name: String, var age: Int) {
// Override the equals method for custom comparison
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || javaClass != other.javaClass) return false
// Check for equality based on name and age
val otherPerson = other as PersonNormalClass
return name == otherPerson.name && age == otherPerson.age
}
// Override the hashCode method to be consistent with the equals method
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + age
return result
}
}
fun main() {
// Create two instances of PersonNormalClass
val person1 = PersonNormalClass("John", 25)
val person2 = PersonNormalClass("John", 25)
// Compare instances using equals method
val areEqual = person1 == person2
println("Are the instances equal: $areEqual")
}
Using comparator
import java.util.Comparator
class PersonComparator : Comparator<PersonNormalClass> {
override fun compare(person1: PersonNormalClass, person2: PersonNormalClass): Int {
// Compare based on name and then age
val nameComparison = person1.name.compareTo(person2.name)
if (nameComparison != 0) {
return nameComparison
}
return Integer.compare(person1.age, person2.age)
}
}
fun main() {
// Create two instances of PersonNormalClass
val person1 = PersonNormalClass("John", 25)
val person2 = PersonNormalClass("John", 25)
// Use the Comparator to compare instances
val comparator = PersonComparator()
val comparisonResult = comparator.compare(person1, person2)
// Print the result of the comparison
println("Comparison result: $comparisonResult")
}
Automatically provide component functions for properties, which can be useful in certain scenarios, like destructuring declarations.
val (name, age) = person1 // Destructuring declaration using component functions
There is no such feature using normal class
Provide a copy
method, which allows you to create a copy of an instance with some properties changed.
val modifiedPerson = person1.copy(age = 30)
Such a feature is not there
Data classes automatically generate a no-arg constructor if all the properties have default values.
data class Student(val name: String = "", val age: Int = 0)
This is not possible and you need to manually create it
- It creates a new object, But it does not create new objects that are contained within the object.
- When there are nested level objects, The newly created object will reference the old references for the nested levels
- The newly created object not only is a new reference but the nested levels of the objects will also be the new references.
- Kotlin's
copy()
provides theshallow
copy()
- If you need
deep
copy()
, You need to implement it manually
In this example, modifying person1.address.street
would also affect person2.address.street
because they refer to the same Address object. If you need a deep copy, you'd have to manually create a new Address object during the copy process.
data class Address(val street: String, val city: String)
data class Person(val name: String, val age: Int, val address: Address)
fun main() {
val address = Address("Main St", "Cityville")
val person1 = Person("Alice", 25, address)
val person2 = person1.copy()
// Both person1 and person2 will share the same Address object
println(person1)
println(person2)
}
- A data class can be made immutable, which means that the fields of the class are closed for modification.
- If we try to modify the fields, an exception will be thrown. Immutable data classes are thread-safe since their content cannot be changed.
- When all properties of a data class are declared as val, the instances of that class become immutable, meaning their values cannot be changed after instantiation.
- Since the data held by these classes cannot be modified once created, there is no risk of data corruption or unexpected behavior when accessed by multiple threads concurrently.
data class Person(val name: String, val age: Int)
- In this case, an instance of the Person class, once created, cannot be modified. If you need to change a property's value, you have to create a new instance with the updated values. This immutability guarantees that the data remains consistent and predictable, making instances of the class thread-safe.
- NOTE: However, it's important to note that while immutable data classes provide thread safety for the data they encapsulate, the overall thread safety of your application still depends on how you handle concurrency in other parts of your code, especially when dealing with shared mutable state.