Kotlin Data Classes

Having read about classes and objects in detail, we now can recall of scenarios in our applications where we create certain classes which act as place holder for the data. Along with data these classes contain basic standard functions and utilities. The common functions these class hold are the getters, the setters, the toString method, equals and hashcode.

Let’s take an example of the Employee class which has some basic functionalities available as below:

package dataclass;

public class Employee {
	
	private String Id;
	private String name;
	private int sal;
	
	
	public Employee() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Employee(String id, String name, int sal) {
		super();
		Id = id;
		this.name = name;
		this.sal = sal;
	}
	public String getId() {
		return Id;
	}
	public void setId(String id) {
		Id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getSal() {
		return sal;
	}
	public void setSal(int sal) {
		this.sal = sal;
	}
	@Override
	public String toString() {
		return "Employee [Id=" + Id + ", name=" + name + ", sal=" + sal + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((Id == null) ? 0 : Id.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;
		Employee other = (Employee) obj;
		if (Id == null) {
			if (other.Id != null)
				return false;
		} else if (!Id.equals(other.Id))
			return false;
		return true;
	}
}

Based on the above example, we have seen that how for even a basic functionalities as shown in the example above we need to write at least 50 lines of code, agreed on the fact that major IDE’s today create all these code automatically for us but ultimately this code remains in the boiler plate of source code and adds to the clutter. Even more, let’s take an example of adding a new field in our class say, employee personal mobile number. In such case our code we would need to be restricted and rewritten as below:

package dataclass;

public class Employee {
	
	private String Id;
	private String name;
	private int sal;
	private String perPhNo;
	
	
	public Employee() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	

	public Employee(String id, String name, int sal, String perPhNo) {
		super();
		Id = id;
		this.name = name;
		this.sal = sal;
		this.perPhNo = perPhNo;
	}
	public String getId() {
		return Id;
	}
	public void setId(String id) {
		Id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getSal() {
		return sal;
	}
	public void setSal(int sal) {
		this.sal = sal;
	}
	public String getPerPhNo() {
		return perPhNo;
	}
	public void setPerPhNo(String perPhNo) {
		this.perPhNo = perPhNo;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((Id == null) ? 0 : Id.hashCode());
		result = prime * result + ((perPhNo == null) ? 0 : perPhNo.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;
		Employee other = (Employee) obj;
		if (Id == null) {
			if (other.Id != null)
				return false;
		} else if (!Id.equals(other.Id))
			return false;
		if (perPhNo == null) {
			if (other.perPhNo != null)
				return false;
		} else if (!perPhNo.equals(other.perPhNo))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Employee [Id=" + Id + ", name=" + name + ", sal=" + sal + ", perPhNo=" + perPhNo + "]";
	}
}

Kotlin Data Classes

To solve the above problem, kotlin came up with the concept of data classes.

Kotlin data classes are defined as the classes which contain only the state and performs no functions, to put it in technical terms classes that contain only variables and are not having any functions which perform standalone functions are termed as Data Classes.

Kotlin improves the regular classes when we make them as data classes. In such cases the compiler automatically generates default getters and setters for the properties present in the class (only mutable ones). Not only this, the implementation of standard methods like hashCode(), toString() and equals() is derived automatically based on the properties declared in the class.

Let’s take the previous example of the Employee class and create its data class. It would sum it to only a single line of code:

data class Employee(val id: String, val name: String, val sal:Int)

Now, to create the object of the above Employee we can do it as below:

val emp1 = Employee("1","Kotlin Blogger1",5000)

Accessing the Values:

To access the values of the data class object we can do it in a simple way by using the (.) dot operator as below:

val empName = emp1.name

Based on the above learning lets create and understand a complete example to see the actual working of the data class and after that we will move on forward to see the brief working of the inbuilt methods.

data class Employee(val id: String, val name: String, val sal:Int)

fun main(args: Array<String>) {
	
	// Here the object of Employee is created
	val emp1 = Employee("1","Kotlin Blogger1",5000)
	
	// We can access the object 
	
	val empName = emp1.name
	val empSal = emp1.sal
	val empId = emp1.id
	
	println("Employee's Name : $empName")
	println("Employee's Id : $empId")
	println("Employee's Salary : $empSal")	
	
	println("Entire Employee Details: $emp1")	
}

The output of the above code will be as below, point to ponder here is the emp1 when we pass this object directly, it simply calls the toString() method, well not a reason to worry as we will look into the detail about the functions in the next section.

Employee’s Name : Kotlin Blogger1
Employee’s Id : 1
Employee’s Salary : 5000
Entire Employee Details: Employee(id=1, name=Kotlin Blogger1, sal=5000)

InBuilt Methods

Having gone through the basics of data class, lets quickly have a look at the functions one by one.

  1. The toString() method:

The toString method converts the object to a String, the output of the toString() method is similar to default implementation of Java’s toString() as below:

// we can call the method directly by simply calling the object of the data class as below:
println("Entire Employee Details: $emp1")

Output of the above code will be as:
Employee(id=1, name=Kotlin Blogger1, sal=5000)
  1. Data Class’s equal method:

Kotlin’s equal method works uniquely and can be used interchangeably with ‘==’. Even much to surprise the == operator internally calls the equal() mthod. Lets create two employee object with same values and see their output.

data class Employee(val id: String, val name: String, val sal:Int)

fun main(args: Array<String>) {
	
	val emp1 = Employee("1","Kotlin Blogger1",5000)
	val emp2 = Employee("1","Kotlin Blogger1",5000)
	
	print("The result of comparison of the two employee object is: "+ emp1.equals(emp2))
	}

The output of the above code is as below:

The result of comparison of the two employee object is: true

Now, as seen in the above example, by simply using the method we can actually compare the values of the rather than the reference of the object as is done in Java.

  1. Hashcode() method:

The hashcode() method, as in Java is the one which generates the has code for the class automatically.

data class Employee(val id: String, val name: String, val sal:Int)

fun main(args: Array<String>) {
	
	val emp1 = Employee("1","Kotlin Blogger1",5000)
	println("The Hash Code of the Empployee Object: ${emp1.hashCode()}")	

}
  1. The copy() function:

Before we discus about the copy function, lets have a look at mutable and the imutable properties of the instances of data class. We can define all the properties or variables of the class either using var or using val. The ones defines as var are termed as mutable and the ones termed as val are the immutable properties. It is recommended to keep the instances of the class as immutable.

Since immutable objects are easy to work in case of multi-threaded environments. Owing to this reason it is easier to handle as the worry about concurrency problem when working in a multi- threaded environment. Immutability would stop the object from being modified by the multiple threads at the same time.

With kotlin it is easier to work with immutable data objects as it automatically creates a copy() in all data classes. We can use the copy() function to copy an existing object into a new object and not even that, it even allows us to modify some properties of the new object while keeping the objects unchanged. Lets see how this can be done by the example below:

data class Employee(val id: String, val name: String, val sal:Int)

fun main(args: Array<String>) {
	
	// Here the object of Employee is created
	val emp1 = Employee("1","Kotlin Blogger1",5000)
	
	/* 
The below piece of code will copy the customer object into a separate object but updates the name 
   Existing object of Employee "emp1" remains unchanged.
   */
	val emp2 = emp1.copy(name="Kotlin Blogger2")
	
	
	println("Employee Object: $emp1")
	println("Updated Employee Object: $emp2")
}

The above code will print the two employees of the object but having different names.

Employee Object: Employee(id=1, name=Kotlin Blogger1, sal=5000)
Updated Employee Object: Employee(id=1, name=Kotlin Blogger2, sal=5000)

So, that was all about the Kotlin Data Class, the thing which should be kept in mind is the fact that Data Class cannot be an abstract, open or inner class.

To summarize it is evident that Data Class in Kotlin helps us to reduce the boiler plate code and make classes look clean, crisp and easily understandable providing the ease of development within built functions.

Leave a Comment