Authors Top

If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

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) { = name; = studio;
        this.rating = rating;

    public String getName() {
        return name;

    public void setName(String name) { = name;

    public String getStudio() {
        return studio;

    public void setStudio(String studio) { = studio;

    public float getRating() {
        return rating;

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

    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;

    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 ( != null)
                return false;
        } else if (!name.equals(
            return false;
        if (Float.floatToIntBits(rating) != Float.floatToIntBits(other.rating))
            return false;
        if (studio == null) {
            if ( != null)
                return false;
        } else if (!studio.equals(
            return false;
        return true;

    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 are available:

println(   //Whiplash
println( //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 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 our 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 to 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. Java Records Compatibility

As of Kotlin 1.5, we can make Kotlin data classes compile as Java 14+ records. To make that happen, all we have to do is to annotate the data class with the @JvmRecord annotation:

data class Person(val firstName: String, val lastName: String)

In order to compile this, we can use kotlinc:

>> kotlinc -jvm-target 15 -Xjvm-enable-preview Person.kt

If we’re targeting Java 15 or older versions, we have to enable the preview JVM versions via -Xjvm-enable-preview flag. As of Java 16, however, records are stable Java features. Therefore, if we’re targeting Java 16 or newer versions, we don’t need to enable the preview features:

>> kotlinc -jvm-target 16 Person.kt

Now if we take a peek at the generated bytecode, we’ll see that the Person class extends the java.lang.Record class:

>> javap -c -p com.baeldung.dataclass.Person
Compiled from "Person.kt"
public final class com.baeldung.dataclass.Person extends java.lang.Record {
    // omitted

Since Java records are immutable, we can’t use var declarations for data classes annotated with @JvmRecord:

@JvmRecord // won't compile
data class Person(val firstName: String, var lastName: String)

Here, the compiler will fail with the following error message:

Constructor parameter of @JvmRecord class should be a val

Moreover, this type of data class can’t extend other classes, as it’s already extending the Record superclass.

6. 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 over on GitHub.

Authors Bottom

If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

Comments are closed on this article!