What is the difference between a normal class and a data class in Kotlin?

Kotlin

Kotlin Problem Overview


I tried to resolve task #6 (DataClass) at Kotlin Koans. When I used the normal class in code, the test case failed.

Here's my code of the data class:

data class Person(val name: String, val age: Int)

fun task6(): List<Person> {
    return listOf(Person("Alice", 29), Person("Bob", 31))
}

Here's result of the data class:

[Person(name=Alice, age=29), Person(name=Bob, age=31)]

Here's my code of the normal class:

class Person(val name: String, val age: Int)

fun task6(): List<Person> {
    return listOf(Person("Alice", 29), Person("Bob", 31))
}

Here's result of the normal class:

[i_introduction._6_Data_Classes.Person@4f47d241, i_introduction._6_Data_Classes.Person@4c3e4790]

Does that mean there is difference between a normal class and a data class in Kotlin. If yes, what is that?

Updated:

Thank @Mallow, you are right. That works:

class Person(val name: String, val age: Int) {
    override fun toString(): String {
        return "Person(name=$name, age=$age)"
    }
}

fun task6(): List<Person> {
    return listOf(Person("Alice", 29), Person("Bob", 31))
}

Kotlin Solutions


Solution 1 - Kotlin

Most of the time we developers use class to keep only data in classes. Classes have some methods which needs to be overwritten wrt the data it holds. ex: hashCode(), equals().

Data classes automatically take care of such utilities.

From the official documentation:

>We frequently create a class to do nothing but hold data. In such a class some standard functionality is often mechanically derivable from the data. In Kotlin, this is called a data class and is marked as data.

>The compiler automatically derives the following members from all properties declared in the primary constructor:

> - equals()/hashCode() pair, > - toString() of the form "User(name=John, age=42)", > - componentN() functions corresponding to the properties in their order of declaration, > - copy() function (see below). If any of these functions is explicitly defined in the class body or inherited from the base types, it will not be generated.

To read more, check data-classes

About the result, Technically, you are getting is different because of implementation of toString() method. data class' toString() method uses data class properties and values to form returning string. General class' toString() method uses hash code to form returning string.

Solution 2 - Kotlin

for a data class.

> The compiler automatically derives the following members from all > properties declared in the primary constructor: > > equals()/hashCode() pair, > > toString() of the form "User(name=John, age=42)", > > componentN() functions corresponding to the properties in their order > of declaration, > > copy() function (see below).

see https://kotlinlang.org/docs/reference/data-classes.html

Solution 3 - Kotlin

A class represents some data "type" and its behaviour(s) so from that point of view data class isn't any different than a class. But there are certain behaviours and rules about a data class that makes it a bit different:

  1. Calling toString() on a data class dumps a string with all its member properties.
  2. It has componentN method that get member properties by their order n.
  3. It has a copy method which takes the member properties as parameters for making a diff copy of the object.
  4. A data class can not be open. Cant be inherited.
  5. It can not be abstract.
  6. It can not be nested, inner or sealed.
  7. Although it can inherit, define abstract methods and implement interfaces.
  8. data class properties can be destructed into individual variables e.g val (name, address) = Person("name", "address")
  9. Pair(a, b) internally uses data class.

Solution 4 - Kotlin

It is very common to create classes whose main goal is to hold data. If you want your class to be a convenient holder for your data you need to override the universal object methods:

Note: equals() is used for structural equality and it is often implemented among with hashCode().

Usually, the implementation of these methods is straightforward, and your IDE can help you to generate them automatically. However, in Kotlin, you don't have to general all of these boilerplate code. If you add the modifier data to your class, the necessary methods are automatically added for you.

The return value of toString() will have the format ClassName(parm1=value1, param2=value2, ...). equals() and hashCode() methods take into account all the properties declared in the primary constructor.

The copy() method

When you mark a class as a data class, the method copy() is also automatically generated which allows you to make copies of an existing instance. This feature is very handy when you are using your instances as keys for a HashMap or if you are dealing with multithreaded code.

Even though the properties of a data class are not required to be val, i.e., you can use var, it is strongly recommended that you use read-only properties, so that you make the instances immutable.

Finally, componentN() functions corresponding to the properties in their order of declaration are also generated by the compiler when you mark a class as a data class.

Sample Code
class PersonClass(val name: String, val age: Int)
data class PersonDataClass(val name: String, val age: Int)

>>> val ron = PersonClass("Ron", 18)
>>> val harry = PersonDataClass("Harry", 17)
>>> println(ron) // notice the string representation of a regular class
PersonClass@3b6eb2ec
>>> println(harry) // notice the string representation of a data class
PersonDataClass(name=Harry, age=17)
>>> val harryClone = harry.copy() // this creates a copy of the object referenced by harry
>>> val hermione = PersonDataClass("Hermine", 16)
>>> harry == harryClone
true
>>> harry == hermione
false

In summary, if you need a holder for data, you should use a data class which means adding the modifier data to your class. This will generate the following methods for you: toString(), equals(), hashCode(), componentN(), and copy(), so you avoid writing boilerplate code. If you use a regular class, you won't have all these "batteries included".

Solution 5 - Kotlin

Data Class contains internal code which we have to override in Java-like Kotlin generates the equals(), hashCode(), and toString()

Kotlin:

data class User(val name: String, val age: String)

Java:

class Student {
    public final String name;
    public final String age;

    public User(String name, String age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object other) {
        
    }

    @Override
    public long hashCode() {
      
    }

    @Override 
    public String toString() {
        return "User(name=" + name + ",age=" + age + ")";
    }
}

Normal Class:

  1. Can be abstract, open, sealed, or inner but not for Data Class
  2. Constructor parameter can be declared without var and val

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTrần Đức T&#226;mView Question on Stackoverflow
Solution 1 - Kotlinchandil03View Answer on Stackoverflow
Solution 2 - KotlinMobilityView Answer on Stackoverflow
Solution 3 - Kotlinf_iView Answer on Stackoverflow
Solution 4 - KotlinlmiguelvargasfView Answer on Stackoverflow
Solution 5 - KotlinJitesh MohiteView Answer on Stackoverflow