Kotlin has been gaining popularity over the years, especially after Google added support for it in 2017, and later made it the official programming language for Android development.
Kotlin adds great features that will blow the minds of old school Java developers. The features are very helpful and make the code cleaner and easier to read.
Here are some of the cool features that are available in Kotlin that are missing in Java:
Single expression functions
Single expression functions are functions that don’t have any body! They can be used when a function must produce an output with a single operation or by a chain of calls.
Example:
fun square(number: Int) = number * number
Default argument values
Functions can declare arguments with default values. So, in case no value is passed, the specified value will be assigned, making those arguments optional.
Example:
fun increment(number: Int, delta: Int = 1) = number + delta x = increment(x) x = increment(x, 10)
Named parameters
You can call a function or a constructor by passing their arguments in the same order and type as declared. Kotlin also has the feature of calling the function by adding the argument name, making the code more intuitive. By using the parameter name, the order of the parameters can be different from the one that is defined.
x = increment(delta = 10, number = x)
This feature is also available for constructors. This is very handy because you don’t need to implement a builder pattern to create complex objects in a more clean and readable way. You can just create an object using the name of their properties. For example, consider the following data class:
data class Point( val x: Float, val y: Float, val z: Float )
You may create a 3d point using a builder pattern like this:
val point = Point.Builder() .withXCoord(1.0f) .withYCoord(2.0f) .withZCoord(3.0f) .build()
Using named parameters, the above code can be replaced by the following. It still remains clear and easy to read, and of course you don’t need to create the Builder class:
val point = Point(z = 3.0f, y = 2.0f, x = 1.0f)
Assign values with conditionals
In Kotlin if is an expression not a statement. This means that it returns a value! This value can directly be assigned to a variable.
A Java like approach for updating a variable value based on a comparison result will look something like this:
var min = x if (y < x) { min = y }
Since Kotlin treats an if keyword as an expression, the above code can be simplified with the following:
val min = if (x < y) x else y
Return if
As we already saw, ifs in Kotlin are expressions. So, they can be used in return statements as well!
fun min(x: Int, y:Int): Int { return if (x < y) x else y }
The above code can be simplified further as we already saw bodiless single expression functions.
fun min(x: Int, y:Int) = if (x < y) x else y
Return try
Speaking of functions, another cool difference is the try keyword. Like with if in Kotlin, try is also an expression. So, the same rules we saw for if apply for try as well.
The returned result will be the last sentence in the try block or the last one in the catch, if there was any caught exception. See below:
val selectedOption: Int? = try { // The result of readIntFromCommandLine will be assigned to selectedOption readIntFromCommandLine(input) } catch (e: NumberFormatException) { null // If input is not a valid number selectedOption will be null }
Stream-less collections
Java 8 introduced the stream API. You can now filter collections, convert them into different types, etc. The caveat is that it is very verbose to work with streams in Java. Check the following Kotlin code:
val products = listOf( Product(name = "Cereal X", price = 2.49, discountPercentage = 10), Product(name = "Cereal Y", price = 3.29, discountPercentage = 5), Product(name = "Cereal Z", price = 2.99, discountPercentage = 20), Product(name = "Cereal W", price = 3.49, discountPercentage = 50) ) val halfPriceOffers = products.filter { it.discountPercentage >= 50 }
That’s it! No mycollection.stream() or any collectors. The collections API in Kotlin already contains map, flatmap, filter, etc. out of the box, and lambda support! This means that you can use functional programming with code that is stuck to old versions of the JDK like 6 or 7.
Null safe collections and strings
While working with collections and strings, it is very common to suffer when it comes to the evil null pointer exceptions. Most of the time, the code is wrapped into an if != null check, adding more complexity and the code could look pretty ugly if it’s packed with null checks all over the place.
In Java 8, the Optional class was introduced. Optional class is a wrapper that can contain or not contain non null values. Instead of flooding the code with null checks, we can use some of the methods provided by the class like orElse or orElseGet. For example:
List<String> validCouponIds = getFilteredCoupons(allCoupons); Optional<List<String>> nullSafeList = Optional.ofNullable(validCouponIds); if (nullSafeList.orElseGet(Collections::emptyList).contains(couponIdToApply)) { // do something }
The code looks cleaner, and instead of checking for not null and then checking if it contains a value, we can just call orElseGet. This is a simple example but we can have situations where we need to check several objects.
In Kotlin, collections and strings have a method called orEmpty() that basically returns an empty collection of String if it is null. So the above code in Kotlin will look like this:
val validCouponIds = getFilteredCoupons(allCoupons) if (validCouponIds.orEmpty().contains(couponIdToApply)) { // do something }
As we can see, this is even cleaner than the Optional approach.
Conclusion
Using Kotlin in existing Java projects is very easy and it can work in conjunction with Spring, Springboot, Mockito and many other popular frameworks, since it compiles with JVM. It can be used not only for Android, but for web services, backend and standalone applications as well. You can also have a mix of Java and Kotlin classes. New code can be written in Kotlin while legacy one remains Java.
We recommend you to start adding Kotlin to your existing projects, especially if you are tied to old JDK versions like 1.6 or 1.7 that don’t have null safe calls, streams or lambda support that Kotlin provides out-of-the-box.
By – Jose Granados, Senior Architect, DigitalOnUs