Page 1 of 2
Kotlin doesn't have structs or records specifically designed for storing data as you might find in other languages but it does have a special type of class that it just right for the job.
Programmer's Guide To Kotlin
Now Available as a Print Book
You can buy it from:
USA and World Amazon.com
Some Chapters Already Available On The Web
- What makes Kotlin Special (Book Only)
- The Basics: Variables, Primitive Types and Functions
- Strings and Arrays
- The Class & The Object
- The Type Hierarchy
Extract Type and its problems
Extract Basic Generics
- Collections, Iterators, Sequences & Ranges
Extract Iterators & Sequences
- Advanced functions
- Anonymous, Lamdas & Inline Functions
**NEW!** Extract Annoymous and Lambda Functions
- Data classes, enums and destructuring
Extract Data Classes
- Exceptions, Annotations & Reflection
Extract Annotation & Reflection
- Working with Java
This is an extract from the chapter about the new features that Kotlin supports to make working with data within a program easier.
If you program in a language other than Java or Kotlin, you may be familiar with structs, structure or records to store data, sometimes along with a few simple methods.
In Java and hence Kotlin there are no structs. Instead, to store data you simply create a class that has the properties corresponding to the data you want to store. For example:
This is a simple class that can be used to store a person’s name and age.
Other languages permit the use of classes in exactly this way so why do they have things like structs?
The answer is for efficiency reasons.
Structs are generally value types, and this can make processing data faster. As long as the overhead of building reference based objects from classes is low there is no need to introduce a second entity capable of storing data.
A data class is just a class that does nothing but store data. Kotlin's primary constructor syntax makes it very easy to create a data class as you simply have to list the properties as val or var in the primary constructor.
Thus the previous example can be written in Kotlin as:
This is slightly simpler and the class so created is exactly the same but you can now initialize the properties using the primary constructor:
Notice that you can create private properties by putting private in front of the parameter and read only properties by using val in place of var.
This data class idiom is so common Kotlin has extended it by recognizing that we use data classes in particular ways.
You can include the modifier data in front of the class definition to create a Kotlin data class. In this case the properties are created as before but the compiler also generates some standard utility methods:
data class MyPersonClass(
With a data class you also have automatically generated methods for:
equals – used for testing equality of content of two data classes
The equals method is used to implement the == relational operator. This tests for equality of type and content. That is, if two instances of the data object have the same data stored in their properties, == is true. Compare this to the === operator which tests for equality of reference i.e. do two variables reference the same object?
Equality is such an important topic that it deserves a section to itself.
hashcode – generates a hash code based on the content
The hashcode method computes a unique hash for every object, but for a data class the hashcode is only computed using the data properties, which means two instances of the class with the same data have the same hashcode. More on this in the section on Kotlin equality.
componentN – access functions used in destructuring.
Basically these are functions that allow other functions to access the data properties without knowing their names. They allow data classes to be used in destructuring – see later.
automatically stores each of the properties in the variables on the left. Notice that assignment is based on property order and not the names involved. See the section on destructuring later in this chapter.
toString – a toString function that uses the content
The default toString method simply consists of the name of class followed by the hash value of the instance e.g:
The generated toString for a data class creates a string with each property and its current value. For example
If you don't put data in front of the class declaration then you still get inherited equals, hashcode and toString methods but these are inherited from the Any class and are very basic – in particular they don't make use of the data properties.
With a data class you get compiler generated custom methods tailored to the data in the class.
For example an instance of the MyPersonClass generates:
MyPersonClass(firstName=Mickey, secondName=Mouse, age=89)
copy – makes a copy of an instance
The copy method will create the new instance for you and set its properties to the same values as the old instance. For example to create a copy of an instance of MyPersonClass all you need is:
The generated copy method has default values set to the current values of the instance being copied.
For example, in the case of the MyPersonClass, copy is defined as:
This means that if you want to change any of the properties during the copy you can by providing new values that override the defaults. For example:
changes just the firstName property.
This is a technique worth remembering.
A fundamental task in programming is to work out when two things are equal. This is especially important in the case of data classes, and it is the reason the system generates a custom equals method for you.
Kotlin has two equality operators == and === and their negation != and !==.
The simpler operator is === which is a reference equality. This just tests to see if two variables are referencing the same object. For example:
println( person1 === person2 )
Prints false as even though the data classes may appear to be equal given they have the same property values, they are different objects that just happen to have the same property values.
The == operator tests for structural equality.
That is, all of the objects have to have identical properties. If this is the case then they are considered equal. Notice that the == operator is used in many other comparison operations – in, contains and so on.
The == operator is implemented by the equals function for the type being compared. If you implement your own classes and you want to compare them then you need to override the equals method that you inherit from Any.
The Kotlin documentation states that any equals method must behave like an equality operator. It should be:
- reflexive a==a is true
- symmetric if a==b is true then b==a
- transitive if a==b and b==c then a==c
There is another less obvious condition. If two objects are equal then their hashCode functions should return the same hash value. What this means is that if you override equals you have to override hashCode as well.