> At the same time, we have adopted Kotlin as well. Interestingly, most of our developers prefer to stay on Java... not because they love Java, but because of familiarity and that they seem to think it's "good enough".
I have been using Kotlin on Android for a few years now, and really enjoy the extra expressiveness and conciseness that the language offers ... but have come to the conclusion its much easier to write unreadable kotlin code than java code.
One of the things I think makes Kotlin hard to read is scoping functions (apply/also/let/run/with). Based on their names alone, the newcomer can't tell what they do (I had to consult a table for weeks on whether they returned the original object, last expression, and whether they passed it, this, etc).
I do love these when used at the right place, right time, and "just enough" but I keep coming into contact with code that nests these 4-5 levels deep:
aaa?.run {
bbb?.run run2@ {
ccc?.run {
ddd?.run {
this@run2.foo()
// whats in scope here urgh
}
}
}
}
Other pathologies I see are making any method (foo), that operates on X, as an extension method X?.foo(). Then the namespace for X gets polluted with all these things.
One of the great things about Kotlin's nullability in the type system is the operators like ?. and ?: ... but often see code like this:
aaa?.bbb?.ccc?.ddd()
Is it safe, yes, yes it is. It won't crash, but what happens if one of these is null, and
should not be? There is nothing in Kotlin forcing people to write this way, but find Java's explicit checking of null made people think more of the "negative case" (throw an exception? etc)
Like anything else, I guess we could use linting rules, it comes to having good taste ... which is not in abundance.
What worries me the most though, is that in the beginning, Kotlin was mostly additive on Java. I read, in biological systems, adding is easier that changing or removing. It seemed like Effective Java + Typed Groovy. Now as Java has "last mover" advantage, and adopts the good parts of Kotlin, I am worried its not just additive, they need interop between different implementations (records vs data classes).