π¨βπ³ 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 ofInner1
and access its member.
Now see #2, we can only create instance ofInner2
using instance ofOuter
as it carries the reference to an object of anOuter
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 HOFTextBuilder
which has a parameter builder which gives the reference of aStringBuilder
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 :)