Kotlin is available in the android market after so many up-gradation and testing. But lots of people are still unaware of the safety and facilities of this road for Android development. Here in this blog my target is to draw attention of developers as well learners on 9 important reasons why to choose Kotlin as a road for the android app development journey.
1. Null Safety
Many programming languages including Java suffers from Null reference exception. In Java, it is equivalent to to NullPointerException or NPE for short.
Kotlin is designed in such as way that we can avoid or eliminate the NullPointerException from our coding.
// First Code
var outputData: String
outputData = null // Compilation error
// Second Code
val userName: String? = null // Nullable type
println(userName.length) // Compilation error
println(userName?.length) // Correct statement
In first code snippet outputData is declared as not nullable type(i.e. it can not accept the null value) but after that we are trying to assign the null value to outputData which will cause a compilation error(i.e. You can not assign the null value to not-nullable field).
In second code snippet userName declared of Nullable type(i.e. String?) and then while printing the length of the userName we are not checking that either userName is null or not which will cause the compilation error.
2. Singleton Class
Singleton class in Java isn’t as easy to implement as it sounds.
public class DemoSingleton {
private DemoSingleton() {
// No Implementation required
}
private static DemoSingleton instance;
public static DemoSingleton getDemoSingletonInstance() {
if (instance == null) {
instance = new DemoSingleton();
}
return instance;
}
}
But this code is dangerous, especially if multiple threads are using it. If two threads access this class at the same time, two instances of this class could be generated. But if you want to make it a full proof Singleton class (in multi threaded environment) then you need to add a few lines of code.
But can anyone imagine Kotlin have made it so simple and secure. Yes its true-You just put down the keyword object in front of a class name and you will get a singleton of that class without extra efforts.
object DemoSingleton
3. Data Classes
In Java, to define a POJO class first, we need to declare all the variables and then define the getters and setters methods to access those variables. Now if we want to compare the object of that POJO class then we need to override the equals() and hashCode() methods or if we want to print that POJO class object as a String then we require to override the toString() method also.
In the end a typical Java POJO class looks like:
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
User user = (User) object;
if (name != null ? !name.equals(user.name) : user.name != null) return false;
return age != 0 ? age == user.age : user.age == 0;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 25 * result + age;
return result;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
But Kotlin has a special way of defining a POJO class, also refer as data class.
A data class is specified by the keyword data when declaring the class definition in Kotlin, it is like defining a POJO class in Java. The difference is that Kotlin will take care of getter and setter methods as well as equals(), hashCode() and toString() methods for you.
data class User(var name: String, var age: Int)
The above data class in Kotlin is the same as the Java pojo class without boilerplate.
4. Smart Casts
In Java, if we want to cast any object or variable to target type then we first check the type of the variable using the instanceof operator and then cast it to the target type like this -
Object object = "The quick brown fox jumped over a lazy dog";
if (object instanceof String) {
// Explicit Casting to `String`
String string = (String) object;
System.out.println("Found a String of length " + string.length());
}
But in Kotlin the compiler tracks theis-checks and explicit casts for immutable values and inserts (safe) casts automatically when needed:
fun main(args: Array<String>) {
val anyTypeList: List<Any> = listOf("Hey", "Demo", 60, "feet", 100.5, "are", "you")
for (value in anyTypeList) {
when (value) {
is String -> println("String: '$value' of length ${value.length} ")
is Int -> println("Integer: '$value'")
is Double -> println("Double: '$value' with Ceil value ${Math.ceil(value)}")
else -> println("Unknown Type")
}
}
}
As you can see the in above Kotlin code snippet is-checks are doing the instanceof check and casting both. So no need to do the casting again like we do in Java.
5. Default and named arguments
In Kotlin, you can provide default values to parameters in function definition as well.
If the function is called with arguments passing, the arguments will be used which is passing. However, if the function is called without passing arguments, default arguments are used.
fun setUserData(userName: String = "", age: Int = 0, city: String = "New York City") {
..........
}
we can call the above function like this:
setUserData("Tommy", 30)
setUserData("Joy", 40, "Paris")
We can also call a function with named arguments and with the help of that we can pass value to the method in any order. For ex: if we try the same with setUserData() function
setUserData(age = 30, userName = "Peter", city = "London")
But this is not the only use-case of named arguments we can also use it in the case where we do not have all the value for parameters which is required by a particular function.
For example:
Lets say if we want to use setUserData() function and while setting the user data, we only know the city of a particular user which is “Paris” then if we pass “Paris” value to setUserData() function without using named arguments, the compiler will try to use “Paris” as the age of that user because the 1st parameter of setUserData() function is age, which will cause a compilation error. But using named argument we can clearly tell the compiler that “Paris” represent that city parameter in setUserData() function.
setUserData("Paris") // Compilation error
setUserData(city = "Paris") // Correct statement
6. Separate interfaces for read-only and mutable collections
Unlike many languages, Kotlin distinguishes between mutable and immutable collections (lists, sets, maps, etc). Precise control over exactly when collections can be edited is useful for eliminating bugs, and for designing good APIs.
For example:
The Kotlin List<out T> type is an interface that provides read-only operations like size, get and so on. Similar to Java, it inherits from Collection<T> and that in turn inherits from Iterable<T>. Methods that change the list are added by the MutableList<T> interface.
We can see basic usage of the list and set types below:
val numbersList: MutableList<Int> = mutableListOf(100, 200, 300)
val readOnlyView: List<Int> = numbersList
println(numbersList) // prints "[100, 200, 300]"
numbersList.add(400)
------------------------------------------------------------
println(readOnlyView) // prints "[100, 200, 300, 400]"
readOnlyView.clear() // -> does not compile
7. Kotlin Android Extensions
Every Android developer pretty much aware of the findViewById() function. There should not be any doubt that it is a source of potential bugs and nasty code which is hard to read and support. While there are several libraries available that provide solutions to this problem, those libraries require annotating fields for each exposed View.
The Kotlin Android Extension plugin allows us to obtain the same experience that we get with some of these libraries, without adding any extra code.
In essence, this allows for the following code:
// Using R.layout.activity_kotlin_main from the 'main' source set
import kotlinx.android.synthetic.main.activity_kotlin_main.*
class KotlinMainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin_main)
// Instead of findViewById<TextView>(R.id.textView)
textView.text = "Hello, world!"
}
}
Android Extensions is a Gradle plugin. You just need to add following in your module’s build.gradle file:
apply plugin: 'kotlin-android-extensions'
8. Coroutines
Kotlin 1.1 introduced coroutines, a new way of writing asynchronous, non-blocking code (and much more). With the help of coroutines, you can write your lines of code one after the other instead of messing around with callbacks.
Lets see how typical coroutine would look like in Android for our needs:
fun coroutineExample() {
launch(UI) { // line 1
val result = async(CommonPool) { // line 2
...do something asynchronous...
}.await() // line 3
processResult(result) // line 4
}
}
So, the above code can be discussed as follows:
- As commented on line 1, In Kotlin a function called launch(UI) { … } which is available in Kotlin Coroutines library will help to execute a Coroutine under the Android UI thread.
- As shown between line number 2 and 3, execution of an asynchronous operation in a separate thread taken from a thread pool (async(CommonPool) { … }) and wait for the asynchronous operation to complete before moving forward (await()). While we wait for the asynchronous operation to complete, our main Android UI thread is completely free because the coroutine inside launch() is suspended, not the UI thread
- When the asynchronous operation inside async() completes, then the coroutine is resumed in the main UI thread and we get the result inside result (the coroutine resumes from here, where we previously suspended the execution)
- processResult() is executed with the result that we’ve previously computed asynchronously between line number 2 and 3.
As you can see, the flow of the coroutine looks exactly as the one of a common synchronous method, so we don’t have to deal with callbacks to get the result.
9. Extension Functions
Kotlin, similar to C# and Gosu, provides the ability to extend a class with new functionality without inheriting from the class or use any type of design pattern such as Decorator.
To declare an extension function, one need to prefix its name with a receiver type, i.e. the receiver type is the Class for which you want to make a function callable with the dot-notation on variables of this type.
fun <T, R> List<T>.isListNotEmpty(block: (List<T>) -> R) {
if (!this.isEmpty()) {
block(this)
}
}
In the above code snippet, the this keyword inside an extension function corresponds to the receiver object (the one that is passed before the dot). Now, we can call such a function on any List<T>:
val list = listOf("1", "2")
list.isListNotEmpty {
......sequence of code
}
In the above code snippet if the list is not empty then only the code of sequence will be executed inside isListNotEmpty {…}. We are calling isListNotEmpty {…} like a regular function although it is not defined anywhere inside the class. That’s the beauty of extensions functions.
Conclusion
Hopefully the reasons discussed above will lead you to switch to Kotlin and believe me, it’s definitely worth your time spend on. Kotlin also helps you to keep your code readable, crisp and robust. Google also announced Kotlin as the first class language for Android development and the open source libraries have also adopted it(For ex: anko, RxKotlin, kotterknife). As Android KTX announced in Google I/O 2018, we can expect a much lesser code and thus getting away from error prone boilerplate code.