The modern backend is here: Kotlin

Kotlin is great. Kotlin makes JVM great again.

Álvaro Panizo Romano
Empathy.co
6 min readOct 10, 2018

--

Wait, you don’t know anything about the history of this new language? Then I suggest you take a minute to put “Kotlin” into your search bar and thousands of resources will appear on how JetBrains, with the support of Google, has developed one of the most modern and promising languages for backend stack.

Number of Kotlin coding lines; evolution on Github

Now, after your little investigation, I’m going try to explain why I think this language is not only the present, but also the future for JVM backend developments.

Why should you start with Kotlin?

  • JetBrains support: IntelliJ is one of IDEs most popular developments when using JVM. It ensures full integration with frameworks such as Spring and dependency platforms such as maven or gradl.
  • Interoperability and java compatibility: The translation and migration of code to Java is simple and intuitive (in IntelliJ we have an automatic conversion tool) and it offers a progressive migration process suitable for large systems.
  • Finally, it provides a language for JVM with modern features! As well as google support, it was made an official language for Android in 2017, and a growing community (albeit still small).

The wonders of Kotlin

Here are some small examples of Kotlin’s features when applied to real cases within EmpathyBroker’s system code.

Make your code smaller

Any piece of code written in Kotlin is much smaller than the ones written in Java, since it’s less verbose. And, as we all know: “less code means fewer bugs”. That means less time spent on programming which also means cost savings on projects.

Here are some examples of code reduction.

Data classes

Forget about getter, setter, hash code and equals.

This is a simple and well implemented Java class (using Google library Preconditions to check nulls) with two attributes:

public class User {  private String uid;
private String sid;
public User(String uid, String sid) {
this.uid = Preconditions.checkNotNull(uid);
this.sid = Preconditions.checkNotNull(sid);
}
public String getSid() {
return sid;
}
public String getUid() {
return uid;
}
}

Here’s the same with Kotlin:

class User(val uid: String, val sid: String)

Impressive huh!

Elvis operator

As I’ll explain later, even though Kotlin has default null-safety variables we can force a variable to be null with ?in its definition. Let’s see a very simple, but infinitely repeated, example in our system to check null values:

public List<String> load(List<String> langs) {
if (langs == null) {
return emptyList();
}
return langs;
}

Choose an Elvis for this case:

fun load(langs: List<String>?): List<String> {
return langs ?: emptyList()
}
Elvis operator

Improving workflow

It’s very usual in Java to check conditions and a specific value is returned in a concrete method but sometimes the results of the expression checked are the same as the method return value. This means that in Java we must therefore do something like this:

protected int setInstanceNumber(String instance) throws Exception {
Optional<Configuration> config = configurationService
.getConfig(instance);
if(config.isPresent()) {
if (instance.equals(SHOP_NAME_1)) {
return 1;
}
else if(instance.equals(SHOP_NAME_2)) {
return 2;
}
else {
return 3;
}
}
else {
throw new Exception("Error with instance name");
}
}

In Kotlin, you can return expressions and use workflow features like this:

@Throws(Exception::class)
fun setInstanceNumber(instance: String): Int {
val config = configurationService
.getConfig(instance)
return if (config.isPresent()) {
when (instance) {
SHOP_NAME_1 -> 1
SHOP_NAME_2 -> 2
else -> 3
}
} else {
throw Exception("Error with instance name")
}
}

Make your code simpler and better

Object-oriented languages are characterized by having an easy syntax to understand, but in many cases the characteristics of language and the obsession to adapt to a real-life model can make the developments become excessively verbose and convoluted. Kotlin can help with this too.

Null safety (but allowed)

By default, in Kotlin all the variables and parameters can never be null, which guarantees the avoidance of failures before compilation.

Null safety compilation error in IntelliJ

As I said before, you can force a value to be null using ?in its definition:

var number: Int?
number = null

And you can use the !!operator to assert nullable values before returning an attribute:

var nullableList: List<Int>? = listOf(1, 2, 3, 4)
nullableList = null
val size = nullableList!!.size

This code throws the exception cause !!operator assert null before giving access to size.

Smart cast and generic types

I could go on at length in this section but I’ll try to be brief and choose a good example that allows us to see the power of the smart cast and the generic typing.

fun addContexts(contextList: Object) {
if(contextList is List<*>) {
for(context in contextList) {
val genericContext: Context? = context as? Context
this.allContexts.add(genericContext)
}
}
}

In this example, we can see a function that receives an Object and checks if it’s a on a list of elements. In which case, we can transform it with secure as? to the generic object to add it to a generic type list.

Extended functions

Kotlin allows us to extend the functionality of existing classes without inheriting from them. You only need to define the function and part of the code and then use it when you need. For example, this is a Java method to generate a hashed key from a String key:

public static String getHashKey(String key) throws       NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashInBytes = md.digest(key.getBytes(StandardCharsets.UTF_8));

StringBuilder sb = new StringBuilder();
for (byte b : hashInBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
//Use itString rawKey = "raw_key";
String hashKey = getHashKey(rawKey);

In Kotlin, we can define this method as an extension and use it from the original object:

@Throws(NoSuchAlgorithmException::class)
fun String.getHashKey(): String {
val md = MessageDigest.getInstance("MD5")
val hashInBytes = md.digest(this.toByteArray(StandardCharsets.UTF_8))

val sb = StringBuilder()
for (b in hashInBytes) {
sb.append(String.format("%02x", b))
}
return sb.toString
}

//Use it
var hashKey = "raw_key".getHashKey;

Destructuring objects

Kotlin allow functionalities to destruct objects into a number of variables. This is really useful to be able to generate features into the code. This is a simple example to return two numbers of operations inside a method, in Java this is one solution to return two objects:

private Integer[] updateDocuments(InstanceConfig instanceConfig, Date date) {
Integer fieldsUpdated = 0, productsUpdated = 0;
Integer[] results = new Integer[2];

Map<Query, List<FieldPreference>> lastFields = getLastFields();
ProductPreference lastProducts = getLastUniqueProducts();

updateLastFieldsDocuments(instanceConfig, lastFields, lastProducts);
fieldsUpdated += lastFields.size();
productsUpdated += lastProducts.getProducts().size();

results[0] = fieldsUpdated;
results[1] = productsUpdated;

return results;
}

This Java implementation allows values to be returned using an array, Kotlin can improve on this with destructuring and data classes.

data class Result(val fieldsUpdated: Int, val productsUpdated: Int)

fun updateDocuments(instanceConfig: InstanceConfig, date: Date): Result {
var fieldsUpdated: Int? = 0
var productsUpdated: Int? = 0
val lastFields = getLastFields()
val lastProducts = getLastUniqueProducts()

updateLastFieldsDocuments(instanceConfig, lastFields, lastProducts)
fieldsUpdated.plus(lastFields.size)
productsUpdated.plus(lastProducts.getProducts().size)

return Result(fieldsUpdated, productsUpdated)
}

// Now, to use this function:
val (fieldsUpdated, productsUpdated) = updateDocuments(...)

What comes next?

In beta (with a strong background) Kotlin has some incredible features that are summarized below:

  • Coroutines: It’s the natural evolution of the futures of scala and Java8. It’s main advantage in this respect is syntax which avoids many of the issues of the return of the call but it also allows for direct control over the context and the cancellation of work can be highlighted.
  • Kotlin native: As I said, Kotlin is interoperable with Java and allows the mixing of projects using Java code with Kotlin code. As it generates JVM code it’s compatible with Android from whom it gets its official language status.
    Formerly with iOS you could use RoboVM, but for some time the Kotlin / Native beta project was opened, which allows you to generate native executables for desktop and also for iOS.
    Using GWT you can also mix Java code with Kotlin when we want to generate Javascript. Furthermore, with pure Kotlin code you can generate Javascript directly without the need for GWT.
    In the not too distant future it’s also likely that IKVM will be able to generate the bitcode of the .NET platform, including Windows 10 and Windows Phone and Unity.

So…

--

--