Kotlin data class init

Data classes

It is not unusual to create classes whose main purpose is to hold data. In such classes, some standard functionality and some utility functions are often mechanically derivable from the data. In Kotlin, these are called data classes and are marked with 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).

To ensure consistency and meaningful behavior of the generated code, data classes 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.

Additionally, the generation of data class members follows these rules with regard to the members’ inheritance:

  • If there are explicit implementations of equals() , hashCode() , or toString() in the data class body or final implementations in a superclass, then these functions are not generated, and the existing implementations are used.
  • If a supertype has componentN() functions that are open and return compatible types, the corresponding functions are generated for the data class and override those of the supertype. If the functions of the supertype cannot be overridden due to incompatible signatures or due to their being final, an error is reported.
  • Providing explicit implementations for the componentN() and copy() functions is not allowed.

Data classes may extend other classes (see Sealed classes for examples).

On the JVM, if the generated class needs to have a parameterless constructor, default values for the properties have to be specified (see Constructors).

Properties declared in the class body

The compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body:

Only the property name will be used inside the toString() , equals() , hashCode() , and copy() implementations, and there will only be one component function component1() . While two Person objects can have different ages, they will be treated as equal.

Copying

Use the copy() function to copy an object, allowing you to alter some of its properties while keeping the rest unchanged. The implementation of this function for the User class above would be as follows:

You can then write the following:

Читайте также:  Php read only properties

Data classes and destructuring declarations

Component functions generated for data classes make it possible to use them in destructuring declarations:

val jane = User(«Jane», 35) val (name, age) = jane println(«$name, $age years of age») // prints «Jane, 35 years of age»

Standard data classes

The standard library provides the Pair and Triple classes. In most cases, though, named data classes are a better design choice because they make the code more readable by providing meaningful names for the properties.

Источник

Introduction to Data Classes in Kotlin

Introduction to Data Classes in Kotlin

While building any application, we often need to create classes whose primary purpose is to hold data/state. These classes generally contain the same old boilerplate code in the form of getters , setters , equals() , hashcode() and toString() methods.

Consider the following example of a Customer class in Java that just holds data about a Customer and doesn’t have any functionality whatsoever —

public class Customer  private String id; private String name; public Customer(String id, String name)  this.id = id; this.name = name; > public String getId()  return id; > public void setId(String id)  this.id = id; > public String getName()  return name; > public void setName(String name)  this.name = name; > @Override public boolean equals(Object o)  if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Customer customer = (Customer) o; if (id != null ? !id.equals(customer.id) : customer.id != null) return false; return name != null ? name.equals(customer.name) : customer.name == null; > @Override public int hashCode()  int result = id != null ? id.hashCode() : 0; result = 31 * result + (name != null ? name.hashCode() : 0); return result; > >

You see, for creating a Simple class with only two member fields, we had to write almost 50 lines of code.

Yes, I know that you don’t need to write that code yourself and any good IDE can generate all that boilerplate code for you.

But that code will still be there in your source file and clutter it. Moreover, whenever you add a new member field to the Class, you’ll need to regenerate/modify the constructors, getters/setters and equals()/hashcode() methods.

You can also use a third party library like Project Lombok to generate getters/setters , equals()/hashCode() , toString() methods and more. But there is no out of the box solution without any library that can help us avoid these boilerplate codes in our application.

Kotlin has a better solution for classes that are used to hold data/state. It’s called a Data Class. A Data Class is like a regular class but with some additional functionalities.

With Kotlin’s data classes, you don’t need to write/generate all the lengthy boilerplate code yourself. The compiler automatically generates a default getter and setter for all the mutable properties, and a getter (only) for all the read-only properties of the data class. Moreover, It also derives the implementation of standard methods like equals() , hashCode() and toString() from the properties declared in the data class’s primary constructor.

For example, The Customer class that we wrote in the previous section in Java can be written in Kotlin in just one line —

data class Customer(val id: Long, val name: String)

Accessing the properties of the data class

The following example shows how you can access the properties of the data class —

val customer = Customer(1, "Sachin") // Getting a property val name = customer.name

Since all the properties of the Customer class are immutable, there is no default setter generated by the compiler. Therefore, If you try to set a property, the compiler will give an error —

// Setting a Property // You cannot set read-only properties customer.id = 2 // Error: Val cannot be assigned

Let’s now see how we can use the equals() , hashCode() , and toString() methods of the data class-

1. Data class’s equals() method

val customer1 = Customer(1, "John") val customer2 = Customer(1, "John") println(customer1.equals(customer2)) // Prints true

You can also use Kotlin’s Structural equality operator == to check for equality. The == operator internally calls the equals() method —

println(customer1 == customer2) // Prints true

2. Data class’s toString() method

The toString() method converts the object to a String in the form of «ClassName(field1=value1, field2=value)» —

val customer = Customer(2, "Robert") println("Customer Details : $customer")
# Output Customer Details : Customer(id=2, name=Robert)

3. Data class’s hashCode() method

val customer = Customer(2, "Robert") println("Customer HashCode : $customer.hashCode()>") // Prints -1841845792

Apart from the standard methods like equals() , hashCode() and toString() , Kotlin also generates a copy() function and componentN() functions for all the data classes. Let’s understand what these functions do and how to use them —

Data Classes and Immutability: The copy() function

Although the properties of a data class can be mutable (declared using var ), It’s strongly recommended to use immutable properties (declared using val ) so as to keep the instances of the data class immutable.

Immutable objects are easier to work with and reason about while working with multi-threaded applications. Since they can not be modified after creation, you don’t need to worry about concurrency issues that arise when multiple threads try to modify an object at the same time.

Kotlin makes working with immutable data objects easier by automatically generating a copy() function for all the data classes. You can use the copy() function to copy an existing object into a new object and modify some of the properties while keeping the existing object unchanged.

The following example shows how copy() function can be used —

val customer = Customer(3, "James") /* Copies the customer object into a separate Object and updates the name. The existing customer object remains unchanged. */ val updatedCustomer = customer.copy(name = "James Altucher") println("Customer : $customer") println("Updated Customer : $updatedCustomer")
# Output Customer : Customer(id=3, name=James) Updated Customer : Customer(id=3, name=James Altucher)

Data Classes and Destructuring Declarations: The componentN() functions

Kotlin also generates componentN() functions corresponding to all the properties declared in the primary constructor of the data class.

For the Customer data class that we defined in the previous section, Kotlin generates two componentN() functions — component1() and component2() corresponding to the id and name properties —

val customer = Customer(4, "Joseph") println(customer.component1()) // Prints 4 println(customer.component2()) // Prints "Joseph"

The component functions enable us to use the so-called Destructuring Declaration in Kotlin. The Destructuring declaration syntax helps you destructure an object into a number of variables like this —

val customer = Customer(4, "Joseph") // Destructuring Declaration val (id, name) = customer println("id = $id, name = $name") // Prints "id = 4, name = Joseph"

Requirements for Data Classes

Every Data Class in Kotlin needs to fulfill the following requirements —

  • The primary constructor must have at least one parameter
  • All the parameters declared in the primary constructor need to be marked as val or var .
  • Data classes cannot be abstract, open, sealed or inner.

Data classes help us avoid a lot of common boilerplate code and make the classes clean and concise. In this article, you learned how data classes work and how to use them. I hope you understood the all the concepts presented in this article.

Thank you for reading folks. See you in the next post!

Источник

Оцените статью