In part 1 I wrote about what am I doing and why. I also configured Kotlin in the Android project.
It is time to convert some classes to Kotlin. I will start with the model layer located in the data
package. In the data
package we have the Task class. It represents a single TODO item. Tasks are stored in the TasksRepository
class which implements the TasksDataSource
interface. The TasksRespsitory uses TasksLocalDataSource
and TasksRemoteDataSource
to retrieve the tasks from a SQLite database and from a server respectively. The local and remote data sources also implement the TasksDataSource
interface.
Let’s start with the Task class. Before I convert it to Kotlin I want to describe the Task class. It is an immutable class with four fields. The main constructor assigns the parameters to the fields. There are three additional constructors that call the main constructor with some default values. It overrides equals
and hashCode
using functions from Guava
. One of the fields (mCompleted) is excluded from equals
and hashCode
. Overrides toString
printing the task title. It has three other methods. Including comments and the license the file consists of 149 lines. This is basically a value class with an additional field and three additional methods. Most of the code in this class can be generated for us.
Code -> Convert Java File to Kotlin (Shift + Ctrl + Alt + K) will convert the code to Kotlin. After the automatic conversion the code looks like this. The code compiles and the unit tests are passing. After manually fixing some stuff the code looks like this.
The Task.kt
class is an immutable class. It has one primary constructor and one secondary constructor. It overrides equals
and hashCode
. It overrides toString
printing the task name. It also has the three additional properties. The auto-converted class has 102 lines including comments.
Let’s see how Kotlin helps us to eliminate some of the boilerplate in Java.
1
2
3
4
@JvmOverloads constructor(val title: String?,
val description: String?,
val id: String = UUID.randomUUID().toString(),
val isCompleted: Boolean = false) {
The primary constructor for the Task
class is part of the class header. It has four parameters just like the Java version. Unlike the Java version two of them have default values. The compiler will generate a property for each of the parameters in the main constructor and will assign the values from the parameters to the properties. Classes in Kotlin can’t have fields instead we use properties. This primary constructor in Kotlin replaces: the field declarations, three constructors and four getters from the Java version. Sweet.
In a pure Kotlin project this primary constructor would replace the four Java constructors. For this class the @JvmOverloads
annotation and a secondary constructor are used to maintain compatibility with Java. The primary constructor plus @JvmOverloads
replace three of the Java constructors:
1
2
3
4
5
6
7
8
//Replaced by the primary constructor in Kotlin
public Task(@Nullable String title, @Nullable String description)
public Task(@Nullable String title, @Nullable String description, @NonNull String id)
public Task(@Nullable String title, @Nullable String description,
@NonNull String id, boolean completed)
//Replaced with the secondary Kotlin constructor
public Task(@Nullable String title, @Nullable String description, boolean completed)
For the fourth Java constructor is replaced with a secondary Kotlin constructor.
1
2
3
constructor(title: String?, description: String?, completed: Boolean) :
this(title, description,UUID.randomUUID().toString(), completed) {
}
The getTitleForList
function from the Java version is replaced with a titleForList
property. I modified the code to use if
as an expression (it returns a value). Kotlin does not have the ternary operator ? :
because it’s function is achieved with a simple if else
.
1
2
val titleForList: String?
get() = if (!Strings.isNullOrEmpty(title)) title else description
The isActive
and isEmpty
functions are also replaced with properties. Nothing interesting happens here.
After converting the class to Kotlin there was a warning from IntelliJ. The parameter name in the equals
method was named o
just like in the Java version. The parameter name in the equals method of Any
(similar to Object
in Java) is other
. This can cause a problem the function is called using named parameters. IntelliJ offers a quick fix so fixing it is trivial.
The implementations of toString
and hashCode
are the same as the Java version.
While writing part 2 I ignored a very very important Kotlin feature: Null Safety.
This article would not be complete if I don’t mention data classes in Kotlin. It makes sense to make Task
a data class. For this class the default implementation for equals
and hashCode
would not work because isCompleted
is excluded. This can be overridden and the code would end up pretty much the same as now. Also the toString
implementation in this case is different than the standard one. The benefit of making Task
a data class would be in the generated componentN
functions and copy
function.
The componentN
functions are useful in destructing declarations and copy
is useful for creating a new Task with modified fields.
1
2
3
4
5
6
7
8
9
10
11
12
val task = Task("Finish Part 2", "Finish the article and publish it on the blog")
//destructing declaration
val (name, description) = task
//the value of name is "Finish Part 2"
//the value of description is "Finish the article and publish it on the blog"
//copy
val part3task = task.copy(name = "Finish Part 3")
//part3task is a task with name "Finish Part 3".
//The other properties are the same as task
I will continue converting the project to Kotlin and writing about it. Until next time.