I’ve been a huge Kotlin proponent for years. The clear syntax, null safety, type inference, coroutines, workings with streams… it’s all very neat. My background is Android programming, and we were stuck with Java 7 for the longest time. It wasn’t until Android SDK 26 (Android 8, Oreo) that we started getting support for Java 8, and not until you could confidently declare that SDK version as your minimum SDK level for your project that this was practically useful for you.
If you’re not interested on the background of Java/Kotlin on Android, or why Android developers are so impressed by Kotlin, you can skip this section.
|Basically no one
At the time of writing, Android SDK 34 supports Java 17. That is still lagging behind a bit - to use Java 17 features you would have to declare SDK 34 as your minimum, and only the latest Pixel devices could run your app, so Java 17 is still outside your reach. According to the official documentation, Android SDK 32 brought support for Java 11. Declaring 32 as your minimum means your app will be compatible with 50.91% of devices as of the time of writing. Declaring Android 26 as your minimum however means you will reach 93.7% devices, so we can say for most purposes you are stuck with nothing fancier than Java 8 if you want to develop for Android today.
Note that devices running on older android version than 5 can’t access the play store anymore, so it doesn’t make sense to target them.
Thus for the longest time, Kotlin was the way to go if you wanted modern language features. Java has been lagging behind on Android since 2011! And only recently was something more modern available for you, bumping you up to 2014 tech.. in 2020.
Just to demonstrate how fucking long ago 2011 is, here’s an excerpt of things that happened back then:
Some things that didn’t exist yet:
This is how far back you have to go to understand in which state we have been programming Java on Android. Naturally, we were starved to be able to type something like
myCollection.forEach(), which we were able to do once we added the Kotlin plugin to our projects!
To conclude, we were stuck with Java 7 up until the corona lockdowns, at which point we could switch over to 2014 technology with Java 8. How long ago was 2014? Whatever.
Kotlin 1.0 was launched in 2016, and Google announced first-class support for Kotlin in Android in 2017.
Tony Hoare called the null pointer his ‘billion dollar mistake’, but he was definitely too hard on himself. Null is really just “absence of a value”, and it’s only by convention that languages shit their pants when coming across it (You could argue whether its better to check-for-null and do nothing instead of crash, or if you would check-for-null and crash instead of doing nothing, but that discussion is out of scope). Either way, Kotlin has the tools for managing this in multiple ways.
First of all you may declare a variable as nullable (non-nullable is the default).
var nullableString: String? = null
var nonNullableString: String = ""
Thus if you know for sure you don’t want any nullable strings lingering around in your method calls, you can declare that you only want non-nullable ones, and the programmer would have to cast his nullable ones when he’s sure they’re not null (which leans into the smartcasting that Kotlin has). However this is not the only tool at your disposal when dealing with null.
var nonNullableString: String = myObject?.someValue?.get() ?: "default"
?. syntax you can access methods and properties on objects that may be null without running into a nullpointer exception. The last one we used here is the elvis operator
?: which lets us define a value to use instead of null.
So where does this fall apart?
It’ll probably sound a bit stupid, because it’s kind of Javas fault, but one of the big features of Kotlin is interoperability with Java, and your Kotlin code can’t know when it would be called by Java code. Java code can throw nulls around anywhere, even to places where Kotlin declares they can’t, so if your Kotlin code ever interacts with Java code, you are no longer safe. You’ll have to go fully Kotlin in your codebase / module to reap the benefits.
Java has had this since Java 8.
Co-routines are neat, and the closest thing you’d get in Java are virtual threads added in JDK 21 ( though previewed already since JDK 19).
By prefixing a class with
data, you get a couple of things for free:
data class Album(val artist: String, val songs:List<Song>)
This generates getters, setters for
equals() methods for you. Just don’t get too attached to OOP concepts - inheritance is disabled for data classes.
However, in JDK 21 we get records, which are pretty close.
Also coming in JDK 21 for Java.
Wait, what? This is a lack of a feature.
And it’s been debated for years.. Seriously. There’s no reason they couldn’t have it, they don’t have it, it hasn’t been added for years and it’s something you would sorely miss if you are used to Java. Seriously Jetbrains, get on it.
That doesn’t really sound so hot, and you’re right. Kotlin has exceptions unchecked by default, meaning you only need to catch for them if you’re interested in doing so. This doesn’t mean your code is less crash-prone, mind you. It just means your compiler and your IDE will not tell you that the little method call you just invoked may throw exceptions, and your code may not be equipped to do so in turn. From what I’ve gathered, this is only done for convenience.
Some arguments thrown around in favor of unchecked exceptions:
catch blocks or don’t do anything useful with the caught exception
This is one of the reasons people say that Kotlin uses unchecked exceptions, but by doing so, Kotlin is implicitly encouraging this practice.
Try-catch is verbose
Java is a verbose language. Understandably, Kotlin is not trying to be Java. You would think that Kotlin could do something similar to its nullsafety in this regard, and still enforce exceptions to be caught…
This is only true if you don’t use exceptions from the standard library, and if you don’t, they should only be used internally in your codebase anyway. Not a very convincing argument.
The argument was that you can extend a class, override a method and remove the
throws clause. You are not enforced to always declare the
throws in your implementation, and by not doing so, your implementation can’t throw the exception. This makes sense - you could have a safer implementation than what an interface would declare, and anyone using your class specifically would not need to catch the exception that you would never throw. If you wanted to rid exceptions in your codebase, you could theoretically wrap them in classes that would deal with them by logging them and otherwise return empty values. I don’t really think this is a very convincing argument for not checking exceptions, though.
Yet again I can’t consider this a strength of Kotlin, unless you favor pretty code over functional code.
So what should you pick for your latest project? I’d say Java. Kotlin is spreading itself thin trying to be everything, but can’t really compare with the stability Java brings. If you load up your Java + Maven project 10 years later you won’t really need to expect any major problems, but if you load up your Kotlin + Gradle project? I wouldn’t expect the same.