Data Classes in Kotlin

1. Overview

The Kotlin language introduces the concept of Data Classes, which represent simple classes used as data containers and do not encapsulate any additional logic. Simply put, Kotlin’s solution enables us to avoid writing a lot of boilerplate code.

In this quick article, we’ll take a look at Data Classes in Kotlin and compare them with their Java counterparts.

2. Kotlin Setup

To get started setting up the Kotlin project, check our introduction to the Kotlin Language tutorial.

3. Data Classes in Java

If we wanted to create a Movie entry in Java, we’d need to write a lot of boilerplate code:

public class Movie {

    private String name;
    private String studio;
    private float rating;

    public Movie(String name, String studio, float rating) {
        this.name = name;
        this.studio = studio;
        this.rating = rating;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getStudio() {
        return studio;
    }

    public void setStudio(String studio) {
        this.studio = studio;
    }

    public float getRating() {
        return rating;
    }

    public void setRating(float rating) {
        this.rating = rating;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;

        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + Float.floatToIntBits(rating);
        result = prime * result + ((studio == null) ? 0 : studio.hashCode());

        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;

        if (obj == null)
            return false;

        if (getClass() != obj.getClass())
            return false;

        Movie other = (Movie) obj;

        if (name == null) {
            if (other.name != null)
                return false;

        } else if (!name.equals(other.name))
            return false;

        if (Float.floatToIntBits(rating) != Float.floatToIntBits(other.rating))
            return false;

        if (studio == null) {
            if (other.studio != null)
                return false;

        } else if (!studio.equals(other.studio))
            return false;

        return true;
    }

    @Override
    public String toString() {
        return "Movie [name=" + name + ", studio=" + studio + ", rating=" + rating + "]";
    }
}

86 lines of code. That’s a lot to store only three fields in a simple class.

4. Kotlin Data Class

Now, we’ll create the same Movie class, with the same functionalities, using Kotlin:

data class Movie(var name: String, var studio: String, var rating: Float)

As we can see, that’s massively easier and cleaner. Constructor, toString(), equals(), hashCode(), and additional copy() and componentN() functions are generated automatically.

4.1. Usage

A data class is instantiated the same way as other classes:

val movie = Movie("Whiplash", "Sony Pictures", 8.5F)

Now, the properties and functions of are available:

println(movie.name)   //Whiplash
println(movie.studio) //Sony Pictures
println(movie.rating) //8.5

movie.rating = 9F

println(movie.toString()) //Movie(name=Whiplash, studio=Sony Pictures, rating=9.0)

4.2. Copy Function

The copy() function is created, in case that we need to copy an object altering some of its properties but keeping the rest unchanged.

val betterRating = movie.copy(rating = 9.5F)
println(betterRating.toString()) // Movie(name=Whiplash, studio=Sony Pictures, rating=9.5)

Java doesn’t provide a clear, native way for copying/cloning objects. We could use the Clonable interface, SerializationUtils.clone() or a cloning constructor.

4.3. Destructuring Declarations

Destructuring Declarations allow us to treat objects properties as individual values. For each property in out data class, a componentN() is generated:

movie.component1() // name
movie.component2() // studio
movie.component3() // rating

We can also create multiple variables from the object or directly from a function – it’s important to remember about using brackets:

val(name, studio, rating) = movie

fun getMovieInfo() = movie
val(namef, studiof, ratingf) = getMovieInfo()

4.4. Data Class Requirements

In order to create a data class, we have to fulfill the following requirements:

  • The primary constructor needs to have at least one parameter

  • All primary constructor parameters need to be marked as val or var

  • Data classes cannot be abstract, open, sealed or inner

  • (before 1.1.) Data classes may only implement interfaces

Since 1.1, data classes may extend other classes.

If the generated class needs to have a parameterless constructor, default values for all properties have to be specified:

data class Movie(var name: String = "", var studio: String = "", var rating: Float = 0F)

5. Conclusion

We’ve seen Data Classes in Kotlin, their usage and requirements, the reduced amount of boilerplate code written, and comparisons with the same code in Java.

If you want to learn more about Kotlin, check articles such as Kotlin Java Interoperability and the already mentioned Introduction to the Kotlin Language.

The full implementation of these examples can be found in our GitHub project.

Leave a Reply

Your email address will not be published.