Kotlin

Good And Bad Practices Of Coding In Kotlin

Google+ Pinterest LinkedIn Tumblr

Today I decided to write about a couple of things, in my opinion, good and bad practices of coding in Kotlin programming language that you meet often.

Table of Contents:

The use extension function to release resources

Sometimes it’s required to write code to manage resources properly. It’s often the case when working with files, database connections, sockets or other entities with unmanaged resources. For example, you have some file which you open and process it.

fun main () {
   val file = File("/my_file")
   val writer = file.printWriter()
   writer.println("Hello World")
}

Now to properly manage the memory we need to close this file after finishing the job.

fun main() {
   val file = File("/my_file")
   val writer = file.printWriter()
   writer.println("Hello World")
   writer.close()
}

Doing it this way is better than not doing it at all. What if an exception occurs while processing the file? In that case, writer.close will never execute. We can handle this with exception-handling syntax.

fun main {
   val file = File("/my_file")
   val writer = file.printWriter()
   try {
         writer.println("Hello World")
   } catch(__: Exception){
        // handle exception
   } finally {
       writer.close()
   }
}

In the example above the finally block successfully closes the resource whether an exception is thrown or not.

The other way of how we can avoid the crash and not forgetting to close the resource with the Closeable.use extension function.

fun main () {
   val file = File("/my_file")
   file.printWriter().use {
       it.println("Hello World")
   }
}

The .use extension function executes the given block on the current Closeable object and then close it down correctly whether an exception is thrown or not. We combine both the exception-handling and resource management with the Closebable.use extension method.

Note: This will only work if the resource class implements the Closeable interface.

Another example with JDBC connection.

fun insertUser(user: User, connection: Connection) : Int {
   val statement = connection.prepareStatement("insert into users (name,age) values (${user.name},${user.age})")
   return statement.use {
        it.executeUpdate()
   }
}

Iterating Over Collection, Sequences, and Mappings

You can implement iterations and for-loops in Kotlin in several ways. Kotlin offers some built-in classes to facilitate it.

In most cases, we use for-loop to get an iterator that yields values like this.

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")

fun main() {
   for(name in list)
     println(name)
}

// Output
Ahsen
Bob
Alex
Jasmine

Sometimes we need both item and index from an iterator:

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")

fun main() {
     for(indexedValue: IndexedValue in list.withIndex())
        println("Index -> ${indexedValue.index} and value -> ${indexedValue.value}")
}

// Output
Index -> 0 and value -> Ahsen
Index -> 1 and value -> Bob
Index -> 2 and value -> Alex
Index -> 3 and value -> Jasmine

We can also apply the destructing declaration to IndexedValue object like this.

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")

fun main() {
     for((index, value) in list.withIndex())
        println("Index -> $index and value -> $value")
}

But what if you want to iterate in the reversed order? Yes, the range is an option.

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")

fun main() {
    val size = list.size
    for(i in size - 1 downto 0)
       println(list[i])
}

// Output
Jasmine
Alex
Bob
Ahsen

Reversing the iterator in a more elegant way:

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")

fun main() {
   for(name in list.reversed())
     println(name)
}

That’s cool! But what if you have to iterate over two or more iterators at the same time? Then maybe you could end up doing this.

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val anotherList = listOf(1, 2, 3, 4)

fun main() {
    val size = list.size
    for(i in 0..size)
       println("Name -> ${list[i]} and number -> ${anotherList[i]}")
}

// Output
Name -> Ahsen and number -> 1
Name -> Bob and number -> 2
Name -> Alex and number -> 3
Name -> Jasmine and number -> 4

The issue with the above code is that when we add a new value to anotherList then our program will crash with ArrayIndexOutOfBoundsException. So, Is there a better solution?

Yes, we can apply zip and get tuples of the corresponding items.

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val anotherList = listOf(1, 2, 3, 4, 5, 6, 7)

fun main() {
     for(pair in list.zip(anotherList))
        println("Name -> ${pair.first} and number -> ${pair.second}")
}

// Output
Name -> Ahsen and number -> 1
Name -> Bob and number -> 2
Name -> Alex and number -> 3
Name -> Jasmine and number -> 4

Now the above program will not crash even if the one iterator has a different count size than the other. Internally the zip method first verify that both iterators have hasNext before trying to retrieve the value.

Again we can apply destructing declaration with zip method.

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val anotherList = listOf(1, 2, 3, 4, 5, 6, 7)

fun main() {
    for((name, number) in list.zip(anotherList))
        println("Name -> $name and number -> $number")
}

Check out the following article, In case you wanna read kotlin list tips and tricks

A couple of ways to combine multiple iterators

So, you want to combine two lists into one, and if the second list contains a number from the first it won’t be added.

val firstList = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val secondList = listOf("Ahsen", "Jack", "Alen", "Jasmine")

fun main() {
    val result = mutableListOf<String>().apply {
        addAll(secondList)
    }
    for(name in firstList)
       if(!secondList.contains(name))
           result.add(name)
    println(result)
}

// Ouput
[Ahsen, Jack, Alen, Jasmine, Bob, Alex]

So, we got what we’re trying to achieve right combine both lists without duplicate items Right?

But we can use a more concise and arguably more readable approach because that’s what Kotlin is about concise!

val firstList = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val secondList = listOf("Ahsen", "Jack", "Alen", "Jasmine")

fun main() {
   val result = firstList.union(secondList)
   println(result)
}

// Output
[Ahsen, Bob, Alex, Jasmine, Jack, Alen]

Safe and Unsafe cast operator

In Kotlin the typecasting is done with as operator and it’ll throw a ClassCastException if the cast is not possible.

const val NAME = "Ahsen"

val x = NAME as Int

fun main() {
    println(x)
}

The moment I hit the run button the compiler throws the following error.

Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

To avoid this exception being thrown even in the case of unsafe typecasting, one can use a safe cast operator as? that returns null on failure:

const val NAME = "Ahsen"

val x = NAME as? Int

fun main() {
    println(x)
}

// Output
null

So, how do you like these best practices when working Kotlin programming language. I hope you enjoyed this post. If you have some questions or some other best practices let me know in the comments section. I’ll try to put them in the above article.

Thank you for being here and keep writing KOTLINISH code.

2 Comments

  1. Thanks for the article! You’ve missed ? in the second example (they are identical):

    val x = NAME as Int

    To avoid this exception being thrown even in the case of unsafe typecasting, one can use a safe cast operator as? that returns null on failure:

    val x = NAME as Int

    • ahsensaeed067 Reply

      Hey Pasha,
      Thanks for letting me know about typo I’ll update the aritcle.

Write A Comment