πŸ‘¨β€πŸ³ Cooking Tasty code in Kotlin 🍴 β€” Part 2

Shreyas Patil

πŸ‘¨β€πŸ³ Cooking Tasty code in Kotlin πŸ΄β€Šβ€”β€ŠPart 2

​ Hey developers πŸ‘‹, welcome back to the second part of the series. In this article, I’ll walk you through some more or advanced recipes to cook your code tasty πŸ˜‹ in Kotlin. ​ To be honest, you can find some concepts here which might be not easy to understand if you’re a beginner or new to the Kotlin. Still, I’ll try my best to explain it with some examples. ​ ​

⭐️ String templates

​

  • Kotlin provides string templates for performing a variety of operations with string.
  • Using it, we can simplify our code and make it even more readable and easy to trace.
  • For example, see this πŸ‘‡ ​ ​ Here you can see we didn’t need + to concatenate to strings. Instead, we can directly include types using $ which replace the value of that variable. See this now πŸ‘‡

We had to include double-quotes in a string. For example, "name": "John Doe". If you see removed code (πŸ”΄) here, we need to use \" to include double quotes in a string and then it becomes hard to read or trace to someone else. ​ Remember how we concatenated multiple or multiline strings in Java? That’s really not easy to trace the code then. Take a look on this snippet πŸ‘‡ ​

Did you notice simplicityπŸ˜ƒ? In Kotlin, we easily handled a multi-line string using """. We don’t even need to append \n. ​ String literals (let’s say special characters like \n) aren’t processed in templates because you actually won’t need this thing. It’s completely considered as a Raw string. ​ There’s more a lot we can do with strings in Kotlin. ​

⭐️ Destructing Declarations

​

  • Kotlin allows us to destructure an object into a number of variables. For example, see this πŸ‘‡ ​ ​ It’s useful when we just want to access members of an instance directly. ​ But wait, it’ll be dangerous ⚠️ if you change the order of members. Let’s say if you write code as πŸ‘‡ ​
val (userName, userId) = getUsers()

​ Then you can see, a user ID will be assigned to userName and name will be assigned to userId. So handle it with care or just use it when you’re sure that the class structure won’t change again. For example, Kotlin provides a class called Pair<A,B> which is a part of the standard library function. Pair has only two members i.e. first and second. So here we are sure that its structure won’t change then we can surely use destructing declarations there πŸ‘‡. ​ ​ If you don’t need any variable, you can achieve it like this πŸ‘‡ ​

val (_, interests) = getUserInterest()

​

⭐️ Sealed Class

​

  • As its name suggests, sealed classes represent restricted class hierarchies.
  • In simple words, sealed classes are extended enums.
  • A sealed class can have subclasses, but all of them must be declared in the same file as the sealed class itself.
  • For example, see this πŸ‘‡ ​

Okay, In the first snippet we have two classes for Result i.e. Success and Failure. We are sure that getResult() will only return an instance of these two classes. Still, if you see when{} expression, we need to add else{} block unnecessarily because code is not sure about instance type of val result. ​ Now look at the second snippet, we declared these classes under sealed class Result {} and it simplified a lot. That’s very clear that Result can have only two types of instances i.e. Success or Failure. ​ We can also read members of instances, see this πŸ‘‡ ​ ​

As you can see above, we can actually access someData of Success and message of Failure. So this is how it works πŸ˜ƒ. It’s a really perfect thing to replace with enums. ​

⭐️ Inner Class

​

  • How to access the value of outer class in nested classes? Let’s say you want to access this scope of a class from nested class. Don’t worry, inner class in Kotlin is here to help you οΏ½οΏ½
  • See this first πŸ‘‡ ​ ​​

There’s an Outer class and it has two nested classes under it i.e. Inner1 and Inner2. As you can see, Inner1 can’t access ❌ the value of an outer class and Inner2 which is declared using keyword inner can access it βœ…. ​

Observe carefully: In main(),
If you see #1, we can directly create instance of Inner1 and access its member.
Now see #2, we can only create instance of Inner2 using instance of Outer as it carries the reference to an object of an Outer class. ​

⭐️ Functional (SAM) Interface

​

  • The functional interface should only have one abstract method i.e. SAM (Single Abstract Method).
  • A functional interface can have more than one non-abstract members but they must have exactly one abstract method.
  • Using functional interfaces, we can leverage SAM conversions that help make our code more concise and readable by using lambda expressions. See the example below, it’s self-explanatory πŸ‘‡ ​ ​​

​ As you can see, If we don’t use a SAM conversion, we need to write code using object expressions. By just adding fun keyword for the interface, we literally made code even more readable πŸ˜ƒ. Isn’t it sweet? 🍫 ​

⭐️ Lambda Expressions

​

  • This is the spiciest feature of a Kotlin 🌢️.
  • These are like anonymous functions and we can treat them as values.
  • You can use lambda expressions if you want to create a function which doesn’t need to be an Interface (as we saw SAM conversions). For example, see this οΏ½οΏ½. Here you don’t actually need to declare a fun interface. ​ ​​

So here you can see, by using lambda we indirectly created functionality for addition. Now let’s take a look at HOFs in Kotlin which is another ♨️ hot topic. ​

⭐️ Higher-Order Functions

​

  • A lambda is an anonymous function. It becomes a Higher-order function when that takes functions as parameters or returns a function.
  • For instance, see this example πŸ‘‡ ​ ​​ ​ Here, onClick() takes a function as a parameter which is executed when the button is clicked. ​ Do you remember scope functions from the previous part? Yep! let{}, apply{}, etc scope functions are HOFs of the standard library of Kotlin. ​ Now see this example πŸ‘‡ ​ ​​ ​ Here we have created a HOF TextBuilder which has a parameter builder which gives the reference of a StringBuilder in the lambda. That’s why we can call the properties of StringBuilder by using this scope. ​ The more you explore Lambas and HOFs, the more you’ll fall in Love ❀️ with the Kotlin. ​

⭐️ Delegates

​

  • This beautiful language provides a way to use Delegated pattern in code 😍.
  • Kotlin provides it natively requiring zero boilerplate.
  • Take a look at this example. ​ ​​

​ Here the by clause in the supertype list for SportsBike and SportsCar indicates that bike and car will be stored internally in objects of these classes and the compiler will generate all the methods of Vehicle that forward to v. ​ You can explore more about it here. Let’s see property delegations. ​

⭐️ Property Delegation

​

  • We can apply delegations for properties or members.
  • We don’t need to implement the same thing, again and again, using this.
  • For example, Kotlin standard library has some property delegates like lazy, observables, etc. See this πŸ‘‡ ​​ ​​

Here we have used lazy delegate where the value gets computed only upon first access. As you can see, when we accessed value in println() for the first time the message is displayed and next time it’s not. ​ Let’s see how to create our own delegations. See this example first πŸ‘‡ ​​ ​​

In the first snippet, as you can see, we wrote a repetitive code for formatting fields but it simplified a lot after adding delegates. We override setValue() and getValue() operator functions of ReadWriteProperty. To learn more about delegates in Kotlin, refer this. ​

⭐️ Ranges

​

  • In Kotlin, we can easily compute range related tasks with much more readability.
  • The operator .. is used in the form of range. See examples below πŸ‘‡ ​ ​​​

As you can see, code is self-explanatory and it doesn’t need an explicit explanation. We can also add support for ranges for custom class types by overriding rangeTo() function. ​

⭐️ Collection Extensions

​

  • The Kotlin standard library has a πŸ’Žtreasure of extension function for Collections 😍.
  • With the help of these functions, can solve complex problems with simple solutions. It also makes code easy to trace, concise and readable. For example, see this code πŸ‘‡ ​ ​​​ ​ As you can see above, the code is self-explanatory using these cool extension functions. Can you just imagine how much code you’ll need to write for the same use case in any other programming language let say Java? ​ There’s many more we can do with these extensions. The more you explore it, you’ll find it interesting πŸ‘“. ​

⭐️ Coroutines

​

  • The most and most HOT πŸ”₯ topic in Kotlin is Coroutine 😍.
  • It allows us to write asynchronous and non-blocking logic in a clean manner. It provides structured concurrency πŸ”€. ​​ ​​​

See this πŸ‘†, we launched three different jobs using launch with different delay and you can see its output. ​ We can also avoid traditional callbacks using Coroutines. Kotlin provides suspend fun for writing blocking calls. ​ ​​​

In the above example, you can see how code is simplified with the use of Coroutines. ​ Want to see again beauty of Coroutines? Let’s say you have to do two things concurrently, how to do that? Here async comes to help. See this πŸ‘‡ ​ ​​​

​ Here you can see that two coroutines execute concurrently. async returns a Deferred i.e. a light-weight non-blocking future that represents a promise to provide a result later. ​ Β 

Coroutine library contains other APIs like Flow for implementing a reactive approach, Channel for communication and many others. Coroutines are one of the HUGE πŸ”Ά topics in Kotlin. The more you explore it, the more you’ll find it interesting 😍. See this reference for more information. ​​ Β 

Yeah! 😍. By including all these ingredients together, that’s how we cooked tasty code with Kotlin πŸ˜‹. I hope you liked this article. ​ Β 

If you liked this article, share it with everyone! πŸ˜„ ​ Sharing is caring! ​ Thank you! πŸ˜ƒ ​ ​ 

Many thanks to Shalom Halbert and Rishit Dagli for helping me to make this better :)