Deep Dive Into Kotlin Data Classes for Android
In this Kotlin data classes tutorial, you’ll learn when and how to use data classes, how they vary from regular classes and what their limitations are. By Kshitij Chauhan.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Deep Dive Into Kotlin Data Classes for Android
20 mins
- Getting Started
- Data Classes
- Declaring Data Classes
- Constructing Data Classes
- Using Data Classes
- Destructuring Declarations
- Value-Based Equality
- Fixing BuildDriversViewModel
- Data Classes in Hash-Based Data Structures
- Copying Data Classes
- Tests
- Extras
- Easy toString()
- Extension Functions
- Data Class Limitations
- Where to Go From Here?
Mobile applications work with a lot of data. Whether from a database or a network API, data is at the heart of modern Android applications. Modeling data in code has traditionally been a complex affair on Android with Java. The language provides few tools to correctly construct, copy and compare data model classes. Luckily, Kotlin data classes on Android make this process easy, concise and fun.
In this tutorial, you’ll build Paddock Builder, an app to create your own Formula 1 team for the 2021 season. You’ll learn about:
- Creating and using Kotlin data classes
- The advantages of data classes over regular classes
- Using data classes with various Android components
- Limitations of data classes in object-oriented programming
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Then, open the starter project in Android Studio to discover Paddock Builder.
Build and run the app. You’ll see the following screen:
Tapping the Build Your Team button allows you to select drivers for your team. But the driver selection doesn’t work, and the data models in the application use regular classes. So, you’ll implement the missing driver and team building functionality. Then eventually migrate the application to use data classes.
Before you do that, you need to learn more about data classes in Kotlin.
Data Classes
In object-oriented programming (OOP), a class contains both properties and functions. However, classes that serve only as data models focus on properties. In such classes, the compiler can derive some functionality from its member properties. Kotlin facilitates this use case with data classes.
Data classes specialize in holding data. The Kotlin compiler automatically generates the following functionality for them:
- A correct, complete, and readable
toString()
method - Value equality-based
equals()
andhashCode()
methods - Utility
copy()
andcomponentN()
methods
To appreciate the amount of functionality a data class provides automatically, compare the following equivalent code snippets.
First, Java:
// Java public final class User { @NotNull private final String name; @Nullable private final String designation; @NotNull public final String getName() { return this.name; } @Nullable public final String getDesignation() { return this.designation; } public User(@NotNull String name, @Nullable String designation) { this.name = name; this.designation = designation; } @NotNull public final String component1() { return this.name; } @Nullable public final String component2() { return this.designation; } @NotNull public final User copy(@NotNull String name, @Nullable String designation) { return new User(name, designation); } @NotNull public String toString() { return "User(name=" + this.name + ", designation=" + this.designation + ")"; } public int hashCode() { String var10000 = this.name; int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31; String var10001 = this.designation; return var1 + (var10001 != null ? var10001.hashCode() : 0); } public boolean equals(@Nullable Object var1) { if (this != var1) { if (var1 instanceof User) { User var2 = (User)var1; if (Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.designation, var2.designation)) { return true; } } return false; } else { return true; } } }
Now, Kotlin:
// Kotlin data class User( val name: String, val designation: String? )
As you can see, it’s incredible how much code data classes can save you from writing. The less code you write, the less code you need to maintain, and the faster you go. If you’re a fan of Formula 1, you will love going fast!
Declaring Data Classes
You declare data classes similar to how you declare regular classes, except:
- The keyword
data
must precede the keywordclass
. - The primary constructor must not be empty, and it should contain only
val
orvar
properties.
Open Models.kt in repository inside the app module, where you’ll find two classes: Driver
and Team
. Refactor this code to use data classes by adding the data
keyword, as shown below:
data class Driver( val id: String, val number: Int, val firstName: String, val lastName: String, val nationality: String, val currentTeamId: String, ) data class Constructor( val id: String, val name: String, val drivers: List<Driver> )
Build and run the app. The project should compile successfully and you should see no changes.
Constructing Data Classes
Data classes can have two types of constructors: primary and secondary.
The primary constructor on a data class can only declare properties. You can optionally create a secondary constructor, but it must delegate to the primary using the this
keyword.
Here’s an example:
// Primary Constructor data class GrandPrix( val name: String, val location: String, val year: Int, val numTeams: Int, ) { // Secondary Constructor constructor( name: String, location: String, year: Int, ): this(name, location, year, 10) }
Open Grid.kt in repository. This file contains the details of all the teams and drivers. Note that refactoring Driver
and Constructor
didn’t break any code here. This is because data classes are constructed like regular classes: by invoking their constructors.
Using Data Classes
The process of selecting a driver doesn’t work well in the current app: There’s no visual feedback to show the selection status of any list item.
To fix this, open DriversList.kt in java ▸ build ▸ driver inside app module. This file contains the RecyclerView adapter responsible for the driver’s list in BuildDriversFragment
. Currently, this adapter doesn’t know whether the user has selected a driver or not. As such, it cannot visually differentiate between selected and unselected drivers.
Create a new data class, DriverWithSelection
, in the same file with the following content:
data class DriverWithSelection( val driver: Driver, val isSelected: Boolean )
You’ll use this data class to differentiate between selected and unselected drivers in the list.
Now, refactor DriverViewHolder
to the following:
@SuppressLint("SetTextI18n") fun bind(driver: Driver, team: Constructor, isSelected: Boolean) { // 1 binding.apply { driverName.text = "${driver.firstName} ${driver.lastName}" driverTeamName.text = team.name driverNumber.text = driver.number.toString() driverContainer.setBackgroundColor(getBackgroundColor(isSelected)) // 2 driverContainer.setOnClickListener { onDriverClicked(driver) } } // getBackgroundColor(isSelected) method definition }
Here, you have:
- Changed the
bind
method to accept a third parameter indicating the selection status of a driver. - Used this property to change the background color.
Now, try to build and run the app. The compilation should fail, as the onBindViewHolder
method of DriversListAdapter
needs refactoring.
Head to the next section to learn how to fix this.