Kotlin Data Class – Tutorial
Do you get tired of writing thousands of lines of code for your POJO data classes in Java? Every Java Programmer at some stage must have taken a note of the number of lines of code they need to write for classes that just need to store some data. Let’s see how a Book.java POJO class looks like:
public class Book {
private String name;
private String authorName;
private long lastModifiedTimeStamp;
private float rating;
private int downloads;
public Book(String name, String authorName, long lastModified, float rating, int downloads) {
this.name = name;
this.authorName = authorName;
this.lastModifiedTimeStamp = lastModified;
this.rating = rating;
this.downloads = downloads;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public long getLastModifiedTimeStamp() {
return lastModifiedTimeStamp;
}
public void setLastModifiedTimeStamp(long lastModifiedTimeStamp) {
this.lastModifiedTimeStamp = lastModifiedTimeStamp;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public int getDownloads() {
return downloads;
}
public void setDownloads(int downloads) {
this.downloads = downloads;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book that = (Book) o;
if (downloads != that.downloads)
return false;
if (name != null ? !name.equals(that.name) :
that.name != null) {
return false;
}
return authorName != null ?
authorName.equals(that.authorName) :
that.authorName == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (authorName != null ?
authorName.hashCode() : 0);
result = 31 * result + downloads;
return result;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + authorName + '\'' +
", lastModifiedTimestamp='" + lastModifiedTimeStamp + '\'' +
", rating='" + rating + '\'' +
", downloads=" + downloads +
'}';
}
}
WOAH! That’s 96 lines of code for just storing 5 fields in an object. We aren’t doing much here besides having getter setters, toString(), equals() and hashCode() methods. With the clean architectures and separation of code practices in our practices, we need to create POJO classes since every project needs to store data somewhere. This can increase the boilerplate code. This is where Kotlin comes to the rescue, with the use of Data Classes. Data Classes is Kotlin’s answer to reducing boilerplate code. The above POJO class can be written in Kotlin in the following way:
data class Book(var name: String, var authorName: String, var lastModified: Long, var rating: Float, var downloads: Int)
THAT’S IT. Kotlin converts a 96 line java code to a single line of code. This is Kotlin’s way of reducing the boilerplate code in your project!
Creating Kotlin Data Class
Following are the requirements for creating Kotlin Data class.
- You need to append the class with the keyword
data
- The primary constructor needs to have at least one parameter.
- Each parameter of the primary constructor must have a val or a var assigned. This isn’t the case with a normal class, where specifying a val or a var isn’t compulsory.
- Data classes cannot be appended with abstract, open, sealed or inner
Kotlin Data Class built-in methods
Kotlin Data class automatically creates the following functions for you.
- equals() and hashCode()
- toString() of the form “Book(name=JournalDev, authorName=Anupam)”
- componentN() functions for each of the parameters in the order specified. This is known as destructuring declarations.
- copy()
Kotlin Data Class Features
Following are some features that a Data Class provides.
- To create a parameterless constructor, specify default values to each of the parameters present in the primary constructor.
- A Data Class allows subclassing(No need to mention the keyword open).
- You can provide explicit implementations for the functions equals() hashCode() and toString()
- Explicit implementations for copy() and componentN() functions are not allowed.
- We can control the visibility of the getters and setters by specifying the visibility modifiers in the constructor as shown below.
data class Book(var name: String,private var authorName: String, var lastModified: Long, var rating: Float, var downloads: Int)
A val parameter won’t have a setter defined implicitly(can’t be done explicitly too!).
Default And Named Arguments in Data Class
Following is our data class:
data class Book(var name: String, var authorName: String, var lastModified: Long, var rating: Float, var downloads: Int)
None of the parameters have a default value set. So we need to set an argument for each of them in the instantiation as shown below.
fun main(args: Array) {
val book = Book("Android Tutorials","Anupam", 1234567, 4.5f, 1000)
}
Let’s set a few default arguments and see how the instantiation changes.
data class Book(var name: String, var authorName: String = "Anupam", var lastModified: Long = 1234567, var rating: Float = 5f, var downloads: Int = 1000)
fun main(args: Array) {
var book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
book = Book("Kotlin")
book = Book("Swift",downloads = 500)
book = Book("Java","Pankaj",rating = 5f, downloads = 1000)
book = Book("Python","Shubham",rating = 5f)
}
Instead of setting each argument, we can set only the non-default ones and the ones which we wish too using the named argument. Using Named Arguments, we can set the 5th argument as the second one by explicitly specifying the parameter name followed by =. Life is so easier this way!
Kotlin Data Class toString() Method
The toString() is implicitly created and prints the argument names and labels for the instance as shown below.
data class Book(var name: String, var authorName: String = "Anupam", var lastModified: Long = 1234567, var rating: Float = 5f, var downloads: Int = 1000)
fun main(args: Array) {
var book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
println(book)
book = Book("Kotlin")
println(book)
book = Book("Swift",downloads = 500)
println(book)
book = Book("Java","Pankaj",rating = 5f, downloads = 1000)
println(book.toString())
book = Book("Python","Shubham",rating = 5f)
println(book.toString())
}
Note: print function implicitly adds a toString().
Kotlin Data Class copy() Method
Copy function is used to create a copy of an instance of the data class with few of the properties modified. It’s recommended to use val parameters in a data classes constructor in order to use immutable properties of an instances. Immutable objects are easier while working with multi-threaded applications. Hence to create a copy of a immutable object by changing only few of the properties, copy() function is handy.
data class Book(val name: String, val authorName: String = "Anupam", val lastModified: Long = 1234567, val rating: Float = 5f, val downloads: Int = 1000)
fun main(args: Array) {
val book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
println(book)
val newBook = book.copy(name = "Kotlin")
println(newBook)
}
//Following is printed in the console.
//Book(name=Android tutorials, authorName=Anupam, lastModified=1234567, rating=4.5, downloads=1000)
//Book(name=Kotlin, authorName=Anupam, lastModified=1234567, rating=4.5, downloads=1000)
Kotlin Data Class equals() and hashCode()
The hashCode() method returns hash code for the object. If two objects are equal, hashCode() produces the same integer result. Hence, equals() returns true if the hashCode() is equal, else it returns a false.
data class Book(val name: String, val authorName: String = "Anupam", val lastModified: Long = 1234567, val rating: Float = 5f, val downloads: Int = 1000)
fun main(args: Array) {
val book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
println("Hashcode is ${book.hashCode()}")
val newBook = book.copy(name = "Kotlin")
println("Hashcode is ${newBook.hashCode()}")
val copyBook = book.copy()
println("Hashcode is ${copyBook.hashCode()}")
if(copyBook.equals(book))
println("copyBook and book are equal")
if(!book.equals(newBook))
println("newBook and book are NOT equal")
}
//Following is printed in the console.
//Hashcode is 649213087
//Hashcode is 1237165820
//Hashcode is 649213087
//copyBook and book are equal
//newBook and book are NOT equal
The first and third object hashcodes are equal hence they are equal. Note: The equals() method is equivalent to == in kotlin.
Destructuring Declarations
componentN() function lets us access each of the arguments specified in the constructor, in the order specified. N is the number of parameters in the constructor.
data class Book(val name: String, val authorName: String = "Anupam", val lastModified: Long = 1234567, val rating: Float = 5f, val downloads: Int = 1000)
fun main(args: Array) {
val book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
println(book.component1()) //Android tutorials
println(book.component2()) //Anupam
println(book.component3()) //1234567
println(book.component4()) //4.5
println(book.component5()) //1000
}
Destructuring declarations allows us to access the arguments as properties from the class object as shown below.
data class Book(val name: String, val authorName: String = "Anupam", val lastModified: Long = 1234567, val rating: Float = 5f, val downloads: Int = 1000)
fun main(args: Array) {
val book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
val (n,a,date,rating,downloads) = book
}
Note: If a visibility modifier such as private is set on any of the arguments, it can’t be accessed in the above function.
data class Book(val name: String,private val authorName: String = "Anupam", val lastModified: Long = 1234567, val rating: Float = 5f, val downloads: Int = 1000)
fun main(args: Array) {
val book = Book("Android tutorials","Anupam", 1234567, 4.5f, 1000)
val (n,a,date,rating,downloads) = book //This won't compile since authorName is private
}
That’s all for quick roundup on Kotlin Data Classes.