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?
Copying Data Classes
Now that you can successfully recruit drivers, it’s time to name your team!
In the app, work your way through the driver selection screen and arrive at the team details screen. You should be able to name your team and see the drivers you selected.
You’ll improve the underlying code with data classes.
Navigate to BuildTeamViewModel.kt in java ▸ build ▸ team inside app module. You’ll find TeamDetails
to hold the name of a team. Refactor it to be a data class instead:
data class TeamDetails(val teamName: String = "")
Then, modify updateTeamName
to use the following code:
fun updateTeamName(newName: String) { val teamDetails = _teamDetails.value _teamDetails.value = teamDetails.copy(teamName = newName) }
Note the use of copy()
on teamDetails
. This is an auto-generated method to create copies of an existing data class instance with modified values. It accepts optional arguments for each property on the class and returns a new instance, modifying only the values passed to it by performing a shallow copy.
Here, you used it to create a new instance of TeamDetails
using the previous class and modified teamName
. Certainly, it’s incredibly useful for copying immutable data classes with many properties.
With this change in place, build and run the app. You should be able to name your team! Go ahead and proceed to the next step.
If everything went well, you should be able to see your new team for the 2021 Formula 1 season!
Tests
Next, you’ll run the tests in the app to confirm everything is working correctly. These tests ensure that the connection between the Repository and the ViewModel work as intended.
So, to run them, select the Unit Tests run configuration in Android Studio:
And then ensure that all tests passed:
Extras
While the tutorial for the app ends here, read on to learn a few more interesting tidbits about data classes. After all, this is a deep dive, and the topic of data classes is very deep.
Easy toString()
The default implementation of the toString()
method of every class returns the class’s name, followed by a hexadecimal representation of its object’s location in memory. This isn’t very useful, and overriding it with a manual implementation requires you to update it every time a property is added/removed from the class.
For example, check out the following code:
class GrandPrix(val location: String) fun main() { val britishGp = GrandPrix("Silverstone") println(britishGp) // Prints "GrandPrix@5451c3a8" }
Now, check out the same example using a data class:
data class GrandPrix(val location: String) fun main() { val britishGp = GrandPrix("Silverstone") println(britishGp) // Prints "GrandPrix(location=Silverstone)" }
The auto-generated toString()
method returns a correct, formatted and standard string representation of an object. This is useful, for example, for logging and debugging, as it helps you view the structure and values of a class in the log itself.
toString()
.Extension Functions
If you want to add functionality to a data class, it can be tempting to add some member methods to it. However, you may be polluting its internals with utility methods. In such cases, consider using extension functions instead to add functionality.
For instance:
// 1 fun Driver.shortRepresentation(): String { return "$number ${lastName.substring(0, 3).toUpperCase()}" } fun main() { val seb = Drivers.SebastianVettel // 2 println(seb.shortRepresentation()) // Prints "5 VET" }
Here in the code block:
-
shortRepresentation
is an extension function - The same extension function is available on the instance of
Driver
class
Data Class Limitations
Data classes have a few limitations when compared to regular classes:
- They have little utility other than holding data. They can’t be
open
,abstract
,sealed
orinner
classes. - The compiler forbids manually implementing
copy()
andcomponentN()
methods. - Any parent interface or class of a data class must not have a
copy()
method. - The
copy()
method returns a shallow copy rather than a deep copy.
Another cost associated with using data classes is the increased method count in the compiled code. While not an issue on modern Android versions, it’s something to consider when developing for older releases.
Where to Go From Here?
You can download the final version of this project using the Download Materials button at the top or bottom of this tutorial.
Congratulations! You learned a lot in this tutorial and now know the ins and outs of Kotlin data classes. If you’re wondering what to learn next, you can read the following resources:
- Official documentation on data classes
- Programming in Kotlin: Fundamentals
- Programming in Kotlin: Functions and Custom Types
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!