Chapter 7 of 12
~12 min read
Data Classes
Replace Java POJOs with a single line. Data classes auto-generate equals, hashCode, toString, copy, and destructuring.
Java POJO vs Kotlin Data Class
Java — 40+ lines
public class User {
private String name;
private String email;
private int age;
public User(String name,
String email, int age) {
this.name = name;
this.email = email;
this.age = age;
}
public String getName() { return name; }
public String getEmail() { return email; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User u = (User) o;
return age == u.age &&
Objects.equals(name, u.name) &&
Objects.equals(email, u.email);
}
@Override
public int hashCode() {
return Objects.hash(name, email, age);
}
@Override
public String toString() {
return "User{name=" + name +
", email=" + email +
", age=" + age + "}";
}
} Kotlin — 1 line
data class User(
val name: String,
val email: String,
val age: Int
)
// ✅ Automatically generated:
// equals() — structural equality
// hashCode() — consistent with equals
// toString() — User(name=Alice, ...)
// copy() — create modified copies
// componentN() — for destructuring Biggest boilerplate win in Kotlin
Data classes are one of the most beloved Kotlin features for Java developers. The 40-line Java POJO becomes a single line without losing any functionality.
The copy() Function
Create a new instance based on an existing one with some fields changed — perfect for immutable state updates:
Kotlin
val alice = User(name = "Alice", email = "alice@ex.com", age = 30)
// Create a modified copy — original unchanged
val olderAlice = alice.copy(age = 31)
val renamed = alice.copy(name = "Alicia", email = "alicia@ex.com")
// In Android ViewModels — immutable state updates:
_uiState.value = _uiState.value.copy(isLoading = false)Destructuring Declarations
Java
User user = getUser();
String name = user.getName();
String email = user.getEmail();
int age = user.getAge(); Kotlin
val user = getUser()
val (name, email, age) = user // destructuring!
// Also works in for loops:
val users = listOf(User("Alice", "a@b.com", 30))
for ((name, email) in users) {
println("$name - $email")
}Data Class Limitations
Important limitations
- Cannot be
abstract,open,sealed, orinner - Only primary constructor parameters participate in generated functions
- Properties declared in the body are NOT included in
equals/hashCode/toString - Should represent pure data — avoid business logic in data classes
Structural Equality
Java
User a = new User("Alice", "a@b.com", 30);
User b = new User("Alice", "a@b.com", 30);
// == checks reference
a == b // false (different objects!)
a.equals(b) // true (if equals() overridden) Kotlin
val a = User("Alice", "a@b.com", 30)
val b = User("Alice", "a@b.com", 30)
// == calls equals() — structural!
a == b // ✅ true
a === b // false (referential — like Java ==)