Kotlin: The Good, The Bad, and The Ugly

AJ Alt
AJ Alt
Aug 19, 2016 · 14 min read

The Good

There’s a lot to like about Kotlin. Some of the obvious features like null safety, property access, and unchecked exceptions are covered at length in other publications, so I won’t repeat them here. Instead, here are some of the less commonly discussed features of Kotlin that I really like.

Automatic conversion of Java to Kotlin

JetBrains’ Java to Kotlin converter integrated into IntelliJ saves a huge amount of time. It’s far from perfect, but it saves you from having to retype mundane code. Without it, migrating code from Java to Kotlin would take significantly longer.

lateinit, Delegates.notNull and lazy

Kotlin’s null safety is great, but due to the way the Android Activity lifecycle is designed, you’ll often find that you have to initialize a property in a callback like onCreate instead of your class’s constructor. Let’s say you have a property that you would like to define like:

val name: String
var name: String? = null
lateinit var name: String
var age: Int by Delegates.notNull<Int>()
val imm: InputMethodManager by lazy { 
getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
}

Functional collection extensions

Kotlin has some great functional extensions methods on collections and iterables. Functions like any, joinToString, and associate can save you from a lot of hand-coded for loops that are necessary in Java.

Named and default function arguments

Named parameters and Default parameters are pretty basic, but they let you cut down on a lot of boilerplate of defining overloads, and replace one of the main use cases of the Builder pattern.

class Presenter(
val okhttp: OkHttp = productionOkHttp(),
val picasso: Picasso = productionPicassoInstance()
) {...}

The Bad

Although Kotlin is great, it’s not perfect. Here are a few aspects of the language that I’m not in love with.

No namespaces

Kotlin allows you to define functions and properties at the top level of your files. That’s a great feature, but it can cause some confusion when combined with the fact that all top level declarations are referenced unqualified from Kotlin. This can sometimes make it difficult to tell what a function is when reading one of its usages.

fun foo() {...}
object FooActions {
fun foo() {...}
}

No static modifier

Following on the previous point, Kotlin has unusual handling of static function and property declarations that are called from Java. It’s not bad, but it feels dirtier than necessary. For example, the Android View class defines some static constants like View.VISIBLE and static methods like View.inflate:

public class View {
public static final int VISIBLE = 0x00000000;
public static final int INVISIBLE = 0x00000004;
public static View inflate(Context context, int resource) {...}
}
class View {
companion object {
@JvmField
val VISIBLE: Int = 0x00000000
@JvmField
val INVISIBLE: Int = 0x00000004
@JvmStatic
fun inflate(context: Context, resource: Int) {...}
}
}
// With annotations:
View.VISIBLE;
//Without annotations:
View.Companion.getVISIBLE();

Automatic conversion of Java to Kotlin

This was the first topic in the list of things I like about Kotlin, and it works well. But because it work so well 80% of the time, many of the cases where it fails can be frustrating.

Required property accessor syntax

Kotlin has the great syntactic sugar called “property accessor syntax” that allows you to call JavaBeans-style getters and setters as if they were a Kotlin property. So for example, you can call the Activity.getContext() method by writing activity.context instead of writing the whole method name. If you use the actual method call in Kotlin, you will get a lint warning telling you to use the property syntax instead.

Method count

Writing code in Kotlin will certainly reduce the number of lines of code in your project. But it will also probably increase the method count of the compiled code, which is of course a drawback if you’re using Android. There are a number of reasons for that, but one of the larger contributors is the way Kotlin implements properties.


The Ugly

And finally, here are two design decisions that the Kotlin team made that I strongly disagree with, and that I don’t expect to change in the future.

SAM conversion and Unit returning lambdas

This one is a really baffling design decision.

public void registerCallback(View.OnClickListener r)
// Java
registerCallback(() -> { /** do stuff */ })
//Kotlin
registerCallback { /** do stuff */ }
fun registerCallback(r: View.OnClickListener)// Kotlin. Note that parenthesis are required now.
registerCallback(View.OnClickListener { /** do stuff */ })
fun registerCallback(r: () -> Unit)
registerCallback(() -> {
/** do stuff */
return Unit.INSTANCE;
})

Closed by default

Every downside to Kotlin I’ve talked about so far are mostly small syntax details that are not quite as clean I’d like, but aren’t a big deal overall. But there’s one design decision that is going to cause a huge amount of pain in the future: All classes and functions in Kotlin are closed by default. It’s a design decision pushed by Effective Java, and it might sound nice in theory, but it’s an obviously bad choice to anyone who’s had to use a buggy or incomplete third-party library.

“Best practices say that you should not allow these hacks anyway”

The arguments for closed inheritance are mostly centered around the “Fragile Base Class Problem”, which is the idea that, if you allow someone to subclass your library code, they could change the way it works, potentially causing bugs. While that’s a possibility, there are lots of way to use a library incorrectly that will cause bugs. If you override some functionality in a class, it obviously your responsibility if you break something.

“People successfully use other languages (C++, C#) that have similar approach”

People also successfully use languages like Python that allow anything at all to be changed at any time. Python has “non-public” methods like _asdict that are documented to by implementation details. It also has name mangled functions like __intern that are harder to accidentally call. You can freely monkey-patch or override any of those functions whenever you want, and Python won’t complain.

“If people really want to hack, there still are ways: you can always write your hack in Java and call it from Kotlin (see Java Interop), and Aspect frameworks always work for these purposes”

This is of course a ridiculous argument. You still can’t override closed Kotlin methods in Java without unacceptable use of reflection, so this doesn’t hold any weight.

Conclusion

Kotlin is overall a great language. It is much less verbose than Java, and has an excellent standard library that removes the need to use a lot of the libraries that make Java life bearable. Converting an app from Java to Kotlin is made much easier thanks to automated syntax conversion, and the result is almost always an improvement. If you’re an Android developer, you owe it to yourself to give it a try.


Keepsafe Engineering

Engineering problems at Keepsafe. Find open source projects at www.github.com/keepsafe.

AJ Alt

Written by

AJ Alt

https://github.com/ajalt

Keepsafe Engineering

Engineering problems at Keepsafe. Find open source projects at www.github.com/keepsafe.