zsmb.coBeta 3



On "From Java to Kotlin and Back Again"

When I first saw this article appear on /r/programming, I thought I'd post it to /r/kotlin to see what everyone would think of it, and I was planning to leave a comment on the post immediately to ask people to play nice, since it was surely going to be a controversial topic. After all, it's nice when someone takes you out of your own little bubble.

But then I actually read the article, and it turned out to be (mostly) a subjective, non-factual, sometimes condescending mess. There are people who made good reasonable criticism of it in both the comments on the original site and in the reddit post linked above - in contrast to those discussions, these here are going to be my completely subjective thoughts, mostly nitpicks and random snarky notes about the article.

Please don't take it too seriously, but I hope you'll enjoy of it.

Name shadowing

In Kotlin, shadowing goes too far. Definitely, it’s a design flaw made by Kotlin team. IDEA team tried to fix this by showing you the laconic warning on each shadowed variable: Name shadowed. Both teams work in the same company, so maybe they can talk to each other and reach a consensus on the shadowing issue? My hint — IDEA guys are right.

The "IDEA team" (or rather the Kotlin plugin team) and the "Kotlin team" will surely turn out to be the same people. I don't think there's internal conflict about whether this is a good idea - it's usually not, but the language allows it in case you need it. If you hate it, you can tweak inspection settings so that it doesn't let your code compile.

Type inference

It was the real advantage over Java. I deliberately said was, because — good news — Java 10 has it and Java 10 is available now.

This is a massive over-simplification. Kotlin's type inference is absolutely everywhere when using the language. It's a joke to call what Java 10 is bringing the same thing. The author notes this, of course:

To be fair, I need to add, that Kotlin is still slightly better in this field. You can use type inference also in other contexts, for example, one-line methods.

But what Kotlin has goes way, way beyond inferring local variable types or return types of functions with expression bodies. These two examples presented here are the ones that people who've just seen their first introductory talk about Kotlin would mention, not people who apparently spent half a year with the language.

For example, how can you not mention things like the way Kotlin can infer generic type parameters? This isn't a one-off feature in Kotlin, it's deeply integrated into the entire language.

Compile time null-safety

This is valid criticism, null safety really is broken when you're interoperating with Java code. The team behind the language has stated countless times that they originally tried making every type coming from Java nullable, but they found that it actually led to worse code.

It's important to see that even here, Kotlin is no worse than Java would be - you just have to pay attention to what you're doing using the given library the same way as if you'd be using it in Java, because it wasn't created with null safety in mind. This isn't something that can just be tacked on after the fact. If a Java library cares about null safety, they have many support annotations they can add.

Perhaps a compiler flag that makes every Java type nullable again could be added, and that would work for the author of this article - but this is no doubt something that the Kotlin team would have to spend significant extra resources maintaining, and it's ultimately something that's been tried and discarded already.

Class literals

Kotlin distinguishes between Kotlin and Java classes and has the syntax ceremony for it

I mean, this is just not true. ::class gives you a KClass instance to be used with Kotlin's own reflection API, while ::class.java gives you the regular Java Class instance for Java reflection.

So in Kotlin, you are forced to write:

val gson = GsonBuilder().registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()).create()

Which is ugly.

I mean, that's just like, your opinion, man. And it's about 7 extra characters in a line of this length.

Reversed type declaration

This disorder is annoying for several reasons.

Just to clear things up, this reversed order exists so that you can (and you usually should) omit explicit types in a sensible way.

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.

It's... Just syntax. A fairly common one in modern languages, actually (see Scala, Swift, etc.).

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?

Well, if you've been writing Kotlin code, basically none?

The third problem with reversed notation is poor auto-completion in an IDE. (...) Typing this variable in Kotlin is harder even in IntelliJ, the greatest IDE ever.

I don't know what IntelliJ you've been using, but I've been getting autocomplete suggestions for both variable names and types all the same, and for parameters IntelliJ even gives you suggestions for both the name and type at the same type, which is actually better than in Java.

Companion object

A Java programmer comes to Kotlin.

“Hi, Kotlin. I’m new here, may I use static members?” He asks.

Java programmers are male.

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.

class AppRunner {
    companion object {
        @JvmStatic fun ​main(args: Array<String>) {
            SpringApplication.run(AppRunner::class.java, *args)
        }
    }
}

It isn't the only way to launch a Java app though. You can just do this:

fun ​main(args: Array<String>) {
    SpringApplication.run(AppRunner::class.java, *args)
}

Or even this:

fun ​main(args: Array<String>) {
    runApplication<AppRunner>(*args)
}

Collection literals

Simply, the neat syntax for collection literals is what you expect from a modern programming language, especially if it’s created from scratch. Instead of collection literals, Kotlin offers the bunch of built-in functions: listOf()mutableListOf()mapOf()hashMapOf(), and so on.

This has been brought up before, and the Kotlin team are considering them. You can already use array literals in annotations. But otherwise, these collections factory functions are fairly concise and they're yet another thing that feels "built-in" to the language while they're actually just library functions.

In maps, keys and values are paired with the to operator, which is good, but why not use well-known : for that? Disappointing.

You've just complained about using : for type declarations a couple paragraphs ago. And again, to has the benefit that it doesn't have to be a separate language construct, it's just a function that anyone could've implemented.

Maybe? Nope

There is no Optional equivalent in Kotlin. It seems that you should use bare Kotlin’s nullable types.

If you love Optional, you can just use it. Kotlin runs on the JVM.

For example, in Java:

public int parseAndInc(String number) {
    return Optional.ofNullable(number)
                   .map(Integer::parseInt)
                   .map(it -> it + 1)
                   .orElse(0);
}

(...)

fun parseAndInc(number: String?): Int {
    return number?.let { Integer.parseInt(it) }
                 ?.let { it -> it + 1 } ?: 0
}

Now, compare readability of the Java and Kotlin versions. Which one do you prefer?

Yes, this really is a bit ugly. But you should not be using parseInt in Kotlin code, but instead do this (again, I'm not sure how you miss this in 6 months of working with the language). And why would you explicitly name a lambda parameter it? Finally, formatting it closer to the Java version helps:

fun parseAndInc2(number: String?): Int {
    return number?.toIntOrNull()
                 ?.let { it + 1 } 
                 ?: 0
}

I'd argue this isn't harder to read than the Java equivalent now.

Data classes

This limitation is not Kotlin’s fault. There is no way to generate the correct value-based equals() without violating the Liskov Principle. That’s why Kotlin doesn’t allow inheritance for Data classes.

I don't know why you'd bring this up at this point. If you need more complex classes, you can still create them and maintain their equals, hashCode, etc. methods by hand. Data classes are just a convenience for a simple use case that comes up very often for a lot of people.

Open classes

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.

Okay, so now we dislike : again.

If you are using Spring, you have two options. You can put open in front of all bean classes (which is rather boring), or use this tricky compiler plugin:

This makes it sound like this some dirty hack. It's an official compiler plugin, and it works great.

Steep learning curve

If you think that you can learn Kotlin quickly because you already know Java — you are wrong. Kotlin would throw you in the deep end. In fact, Kotlin’s syntax is far closer to Scala. It’s the all-in bet. You would have to forget Java and switch to the completely different language.

Uhh... N-no?

Final thoughts

Learning a new technology is like an investment. We invest our time and then the technology should pay off. I’m not saying that Kotlin is a bad language. I’m just saying that in our case, the costs outweighed the benefits.

With these examples here it really feels like you've only scratched the surface of the language, it's hard to imagine how much time you've invested in it.