Rebuttal for Kotlin
Making the JVM a better place.
2018·06·09 · language design · jvm · java
Kotlin is the hot new language on the block by JetBrains, the creator of the Java IDE you most certainly use (or should be using). It brings null saftey and functional programming to a platform that really needs it, the JVM.
Since integrating Kotlin with Java is incredibly simple, companies across the board have begun switching to Kotlin. Working with Java APIs is incredibly easy, so you can keep all your old code while writing new code in a better language.
Well, can we really say that Kotlin is a “better” language? At Allegro, an online Polish retail site, they ventured into the world of Kotlin and they had such a wonderful time that… they went back to Java?
I have a personal love for Kotlin. I started programming Java about seven years ago, and much like many stubborn programmers I never saw a need to switch to Kotlin, until I tried it out. Writing in the language was so clean, so fast, so safe. After spending the last couple of years writing in Kotlin, I never want to go back.
I do, however, recognize that my opinion is not fact, and that other people can have differing opinions. Allegro is a company that tried Kotlin out, but they weren’t satisfied with the results. I’m here to give some counter arguments to the points they provided in their article, “From Java to Kotlin and Back Again”.
Here is the list of points they provided:
- Name shadowing
- Type inference
- Compile time null-safety
- Class literals
- Reversed type declaration
- Companion object
- Collection literals
- Maybe? Nope
- Data classes
- Open classes
- Steep learning curve
I’m going to go through these one by one.
Shadowing was my biggest surprise in Kotlin. Consider this function:
Okay, so in Kotlin, inc(1) prints 2. The equivalent code in Java, won’t compile:
In Kotlin, shadowing goes too far. Definitely, it’s a design flaw made by Kotlin team.
The first thing that I think of when talking about uses for name shadowing is closures. I use shadowing all the time in closures! I could make different scopes for local variables, but that’s a lot more work than just shadowing the name.
You really shouldn’t write any code that puts you into a position where name shadowing is an issue. The example they provided would never happen in a real world scenario, and real world scenarios often prove this feature useful.
As Lobsters user Leonidas says,
In OCaml I use shadowing all the time, since when I shadow a value, I make it impossible to access the previous value by accident. Especially if the type stays the same I never run into the problem where I accidentally pass
numaround where instead I meant to use
new_numby the simple fact that the scoping just prevents me from messing up.
When you use a language that contains name shadowing, you become more concious of it (especially if you have an IDE that annotates name shadows like IntelliJ) and errors related name shadowing become non-existent.
The author notes that Kotlin’s type inference is the same as Java 10’s.
Compile time null-safety
The main point in this section is that “null types are confusing”.
Things get nasty when your Kotlin code has to get along with Java code (libraries are written in Java, so it happens pretty often I guess). Then, the third kind of type jumps in —
T!. It’s called platform type, and somehow it means
T?. Or if we want to be precise,
Twith undefined nullability. This weird type can’t be denoted in Kotlin, it can be only inferred from Java types.
T!can mislead you because it’s relaxed about nulls and disables Kotlin’s null-safety net.
The relaxation that Kotlin gives you with Java APIs allows the user to declare nullability
for types from a platform that doesn’t offer it. As a consumer of a Java API, you specify
the nullability and handle it yourself. If you know a Java API will never return null
(maybe it uses contracts like
@NonNull), Kotlin allows you to use it as such. The
opposite is also true: if your API can return null, you can specify that it returns null.
Consider the following Java method:
Now, you want to call
format(String)from Kotlin. Which type should you use to consume the result of this Java method? Well, you have three options.
They mention using
String! as a third type, but really that’s not how it’s used.
Third approach. What if you just let the Kotlin do the fabulous local variable type inference?
Personally, I’ve never “used” a
T! because they don’t exist in practicallity. Here,
the author is using the result of
Utils.format(text) as a
This leaves you, the consumer, with two options:
T?. How does one go about
deciding which one to choose? Oh yeah, RTFM. Read the documentation and the code
if you want to know how to use a method! You can obviously tell that function can
return null, so you want to mark the return value as nullable.
The author also notes that you can’t call methods or fields from a nullable type without
dealing with the possibility of
null. This is a core language feature, but the author’s
tone seems to indicate that this is a hassle to deal with. In my personal projects, I have
absolutely loved dealing with null types using
Reflection is used in a lot of libraries and Kotlin has a way to obtain the Java Class of an object.
The author thinks that the syntax is too much, and apparently this is enough to revert back
to Java. I personally don’t understand this mentallity. It’s not much more typing than
and we own IDEs with auto completion. This is really a non-issue.
Reversed type declaration
Another stubborn viewpoint of newer languages.
Standard notation in Java:
Reversed notation in Kotlin:
This disorder is annoying for several reasons.
“ahhhh oh noooooo!!! the world is ending!!!!”
It’s just syntax.
First, you need to type and read this noisy colon between names and types. What is the purpose of this extra character? Why are names separated from their types? I have no idea. Sadly, it makes your work in Kotlin harder.
I was somewhat astonished when I first read this. One character makes things that tough?
The colon operator notates that
a is of type A. It makes things rather clean in regards to
In Java 10, we have a keyword taking the place of an identifier! What if I had a class called
var that I wanted to use? (“That would never happen!” - You’ve never dealt with obfuscated
code.) This creates an inconsistency that doesn’t exist in Kotlin.
Here, I can specify the type if I want to. A good example for why this is handy would be with subclasses.
You can do the same in Java, but it’s more consistent in Kotlin.
The second problem. When you read a method declaration, first of all, you are interested in the name and the return type, and then you scan the arguments.
This makes sense, but finding the return value of a function isn’t difficult.
In Kotlin, the method’s return type could be far at the end of the line, so you need to scroll:
Firstly, the name of the function should give some hint to the return value.
Second, this isn’t a problem. I’ve honestly never had this issue while programming in Kotlin. If you have this issue, here are some options for you:
- turn on soft-wrap
- get a bigger monitor
- make your font size smaller
- quit programming, it’s not for you
Or, if arguments are formatted line-by-line, you need to search. How much time do you need to find the return type of this method?
I found the return type instantly because it’s not indented like the arguments are. After using languages
:, you get used to where the types are.
The third problem with reversed notation is poor auto-completion in an IDE. In standard notation, you start from a type name, and it’s easy to find a type. Once you pick a type, an IDE gives you several suggestions about a variable name, derived from selected type. So you can quickly type variables like this:
Typing this variable in Kotlin is harder even in IntelliJ, the greatest IDE ever. If you have many repositories, you won’t find the right pair on the auto-completion list. It means typing the full variable name by hand.
I will say I have experienced this issue before, but I type at 90 WPM so it isn’t an issue for me.
In Kotlin, you have two ways to declare a variable that isn’t tied to the instance of a class:
- companion objects
- top level declarations
The author seems to have completely forgotten the second solution. I am personally fine with writing variables associated with classes in companion objects, but I guess it’s too much for the author.
Sometimes, you have to use static. Old good
public static void main()is still the only way to launch a Java app. Try to write this companion object spell without googling.
Top level declarations still exists. You can throw
main at the top of your file at it’ll run fine.
It’s quite simple.
And the “spell” is rather simple too, once you know what everything does.
In maps, keys and values are paired with the
tooperator, which is good, but why not use well-known
:for that? Disappointing.
Java is absolutely awful when it comes to initializing collections, and Kotlin provides some nice functions to help with that. Instead of providing special syntax for it, Kotlin uses inline functions that are built into the standard library. This cleans up the syntax and saves characters for more important things.
Using functions instead of characters also allows you to specify what data structure you want.
How are you going ot have special syntax for
I’ve been writing a lot of Scala code recently, and I’ve found that Option Data Structures are unsafe
when they provide a
get function. Let’s look at some example code.
All safety is lost when the interface provides such a function. With Kotlin’s nullable types, this state is unrepresentable.
This would create a compile time error.
You can get arround the null safety with
!!, but if you’re using this operator you’re probably
doing something wrong. (It mainly exists for Java interop.)
This will throw an exception at runtime. Using
!! is bad practice, so you shouldn’t run into this
Typically, when you have an Optional, you want to apply a series of null-safe transformations and deal with null at the and.
For example, in Java:
The author goes through some examples of how to convert this into Kotlin, but his final result isn’t clean. Here’s how I’d write it.
This doesn’t account for
Integer.parseInt throwing an exception, but that doesn’t have anything
to do with Kotlin’s null safety.
The author identifies that Data Classes are useful, but have limitations. Not much I can say here.
Inheritance syntax looks like this:
Kotlin changed the extends keyword into the
:operator, which is already used to separate variable name from its type. Back to C++ syntax? For me it’s confusing.
: with inheritence aligns with the usage of it for type annotations. Here, we’re saying
Derived is of type Base, which makes sense:
Derived is inheriting from
I hope I showed that a lot of these “complaints” are really just stylistic choices that some people may be scared of. In reality, experiencing different languages can help you grow as a programmer, and can introduce you to new ways of thinking. It sounds like the people at Allegro were trying to write Java code in Kotlin rather than trying to write Kotlin code. I’ve been in this mindset before, and it’s not a great one. It’s better to accept the paradigms and ideas that a language presents and run with them. If you’ve learned one thing from this article, it’s that you shouldn’t shove one language into another, but you should learn what the new language has to offer.