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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

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.

A screenshot of the "build-screen" team from Paddock Builder

If everything went well, you should be able to see your new team for the 2021 Formula 1 season!

Image showing completed version of "Paddock Builder"

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.

Note: Be careful with logging data classes that contain sensitive data, such as passwords or credit card numbers. You should never log these. Or, at least masquerade the data by overriding 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:

  1. shortRepresentation is an extension function
  2. 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 or inner classes.
  • The compiler forbids manually implementing copy() and componentN() 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:

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!