This is the part 10 of series Java to Kotlin - Learning Simplified. I hope you are learning something new from every part. This part is about Kotlin offerings you will either like it or hate it. I personally like it. And yes, this is last part of our lovely and long series.
If you have not read part 9, please do here.
Index of this Part: Late Init, Delegator, Lazy, Coroutine
lateinit
Kotlin is a strict language. It forces developers to think clearly while architecting. immutability, mutability, nullability, are taken seriously. Every property has to be assigned OR abstract is mantra.
// Kotlin
var value:String β not assigned, not allowed
var value:String? β not assigned, not allowed
As we see, nullable or not, a property has to be assigned. But sometimes, there are cases you need a dependency which is just not yet ready. Tests
are the best example. Another good example is Android View
, you do not have the reference unless view is populated. There are two solutions in that case:
- Make the property mutable and nullable, assign null and later update
- Something better
In the first solution, even though you had assignt he property as soon as you could, the null checks will haunt you. You need to use ?.
OR !!.
wherever you are accessing the nullables even though you are sure you did assign the value.
@Inject
var someDependency:Type? = null
In the above snippet, Inject
is used to inject the dependency but because of assignment rule, we had to make it nullable and assign null. Kotlin offers a solution:
lateinit
As the name says, I, the developer knows I am going to initialise the property before I use. So marking it late initialised
short form lateinit
, so the above example becomes:
@Inject
lateinit var someDependency:Type
There are three points to note here:
lateinit and var
, it only works withvar
and not withval
.val
is immutable, how can you say I will assign something later if fundamentally it can not be changed- No one is stopping you to use the property without initialising it, compiler does not warn you. If you use it before initialised, you get
UninitializedPropertyAccessException
- Custom getter and setter are not allowed with lateinit
My recommendation is not to use this extensively unless you really need it. Having strong null check at compile time saves a lot of headache
What if you need to initialise a val property later?
There could be any reason you can not use var
with lateinit
but you can not assign the val
property immediately. With val
you do not have the option of nullable
+ assign later as well. They are immutable by nature.
π₯ Delegator
to the rescue
Delegator
In simple words, a delegator is an object which can be used instead of getter and setters. This is a Kotlin feature and nothing like it is present in Java. Letβs say you have a property βcanShowBanner which is used to decide whether to show an ad banner to user or not. Now instead of caching the value in your class, you want to delegate this to some common class, e.g
preference manager` in Android. You can do following:
// Kotlin delegator
var canShowBanner: Boolean = SharedPrefDelegator()
// OR if you need read only
val canShowBanner: Boolean = SharedPrefDelegator()
π‘ Important point to note is: Delegated property can not have getter/setter
But how to implement the Delegator
, There is a special syntax for a delegator methods:
// Example
class Delegate {
operator fun getValue(
thisRef: Any?,
property: KProperty<*>
): PropertyType {}
operator fun setValue(
thisRef: Any?,
property: KProperty<*>, value: PropertyType
) {}
}
PropertyType
above is the type of property you will use your delegator with, If used with String, then String, if used with Int, then it will be Int.
The magic is whenever getter
of the property will be called, kotlin will delegate the call to getValue
of your delegator. Similarly, when setter
of the property will be called, Kotlin will delegate the call to setValue
of your delegator.
π‘ Note: Delegator
can be used with var and val both, No need to stress for val, setValue
function is not needed inside your delegator.
Back to the question: What if you need to intialise a val property later?
The answer is Delegator
The keyword to use a delegator is by
You can back your val property with a delegator and everytime you will try to read the val, getValue
of your delegator will be called. Letβs see in action:
// Kotlin
class Delegate {
operator fun getValue(
thisRef: Any?,
property: KProperty<*>
): Int {
}
class Sample{
val prop:Int by Delegate()
}
}
Everytime prop
will be accessed, getValue
of your delegator will be called. It means π€― you can return anyvalue you want from the delegator and your val (even it is immutable) will return the different value. Yea, it takes time to sink in. I have been where you are.
π₯ Interestingly there is a utility in Kotlin called lazy
Lazy
In simple words, lazy is a function which accepts a lambda and returns a delegator. The implementation of the delegator caches the value you return from the lambda amd returns it everytime the property is accessed. I recommend you to read the implementation of Lazy
interface in the kotlin package. Here is a snippet of code:
// Kotlin
val heading:TextView by lazy {
findViewById(R.id.heading)
}
In the above example, heading
is a val but it will be assigned lazily when first accessed. As lazy is just returning a delegator, it can be used with both var and val
Coroutine
I am not exaggerating but if you want only one reason to try Kotlin, it is coroutine
. You must be aware with the concept of threading if you want to get amazed. What Threads did to processes, Coroutines are doing to Threads. I believe this statement is enough in itself to help you understand what a coroutine
is!
Threading 101 is: Process is heavy and threads are light. Process need their own memory space and Threads share the memory space in a process. Co-Routines are even lighter than Threads.
In simple words, we can say co-routines share the threads. Kotlin as a language is offering some thread pools and doing all the heavy lifting for you to share the threads. All you need is to use the syntax and you are doing parallel work in a light weight fashion.
Kindly note this is a huge topic and deserves a series on its own. This section is to get you familiar with it and ignite the spark.
First of all, CoRoutine come with a separate Kotlin dependency. It is in the Kotlin-Core. Ensure you have included this dependency.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x
For Android developers, there is another one as well, It offers Android related implementation like Main Thread Dispatcher:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x
The simplest way to start a co-routine is to use some Coroutine builder, platform provides some of the builders. launch
async
runBlocking
to name a new. Here is one sample:
// Java
new Thread(){
public void run(){}
}.start();
doSomethingHere(); // parallel execution
// Kotlin
GlobalScope.launch {
// do the work in background
}
doSomethingElse()
// this will execute in parallel
You can think the scope as the life of the coroutine, in our case GlobalScope
means life of the program running. However, similar to the threads, main program will not wait for all the threads to complete. It will simply finish on its own. There are other scopes possible like ViewModelScope
in Android.
// Java, start 1 million threads
for(int i=0;i<1000000;i++){
new Thread(){
public void run(){
sleep(1000);
}
}.start();
}
// Kotlin, start 1 million coroutines
repeat(1000000){
launch{
delay(1000)
}
}
Here we started 1 million threads and 1 million coroutines. Thread code might make your computer crazy. Coroutines will run smooth. The reason is sleep literally blocks the thread where delay frees it.
Using Job
launch
returns a Job which can be used to cancel OR join. Check the following code:
// Kotlin
val job = GlobalScope.launch {
// do the work in background
}
job.join() // now main program will wait untill the job (similar to java future)completes
π₯ suspend
keyword
This is one of the unique keyword and feature Kotlin offers. If a function is marked suspend and you call it from a coroutine, the coroutine will not execute the next line after the function call unless the function finishes. There is nothing special till now. The catch is, even if the suspend function is using another async operation inside. The suspended function can invoke another parallel coroutine and the first coroutine will free up the thread untill the suspended function returns. That is what makes coroutines special over Threads. Letβs see an example:
launch{
val user = async { fetchUserFromNetwork() }.await() (similar to java future)
showUser(user)
}
suspend fun fetchUserFromNetwork(){
// time taking operation
}
The code above looks sequential but in reality it is not. Coroutine take care of the heavy lifting and calls showUser only after fetchUserFromNetwork is returned. We could also use async inside the fetch method. There is nothing like this in Java.
You can also say: A coroutine is a light-weight thread. Like threads, coroutines can run in parallel, wait for each other and communicate. The biggest difference is that coroutines are very cheap, almost free: we can create thousands of them, and pay very little in terms of performance. True threads, on the other hand, are expensive to start and keep around. Coroutines are almost free.
Under the hood, Kotlin is using Threads only. So co-routines is not a replacement but just a better management.
I will write another series on coroutines. This section was just to show you that it exists. I recommend you to read official documentation here and here
Summary
We started this part with lateinit
, we saw how can it be used with non-null var properties to assign the value later. Then we saw Delegators
and figured they can be used to delegate get and set
, lazy is a utility which returns a delegator which can be clubbed with both var and val
and it uses the lambda you pass to assign the value. Finally, we touched co-routines
, we just touched tip of the iceburg when it comes to coroutines. I recommend you to learn from official docs. I will write another series on coroutines some day.