From Java to Kotlin and Back Again
allegro.tech
allegro.tech
The author complains a lot about how Kotlin is different from Java. Err.. it is a new language — it is supposed to be different — otherwise why bother?
> I have my favorite set of JVM languages. Java in /main and Groovy in /test are the best-performing duo for me. In summer 2017 my team started a new microservice project, and as usual, we talked about languages and technologies. There are a few Kotlin advocating teams at Allegro, and we wanted to try something new, so we decided to give Kotlin a try.
The opening lines set the tone for the article. The author tried Kotlin because other people wanted to — not because he found it interesting. Most people learn a new language by writing a toy project or two. They don’t start with a real project. And of course it will take time to get used to a new language and be as productive in it as as you were in the old one. The friction is always there, no matter which language you are switching to.
I am sorry, but I can’t take this article seriously — I feel that it’s written for the sole purpose of venting author’s frustrations.
The other problem I have with this post is that it did not state desires/hopes up front. The intro only mentions compile-time null-safety and less boilerplate (for which many languages are better than Java), then adds his desires throughout the text. Thus his complaints seem not a thought through list of failures to satisfy specific desires, but just randomly throwing rocks at vague targets. My 2c.
But is this a main pitch of Kotlin?..
A good example of that tension is the final/open thing: Wonky frameworks abuse inheritance and force you to use open all over the place, but Effective Java suggests you should make every class/method final unless you specifically design it to be extended.
Kotlin advocates try to have it both ways. If it's a full new language, to be evaluated as a full language, then why would you adopt it when it's missing important features compared to Scala? The narrative is that it's a set of small enhancements to Java that are easier to pick up than Scala, but the reality doesn't live up to that.
Simple, they all serve a particular purpose. Scala serves a particular purpose, so does Kotlin.
I have found Kotlin to be an easy to use modern language which the JVM ecosystem was IMO lacking. That said, I am not a Kotlin advocate. I’d use if I had to choose from the JVM languages. Otherwise there are plenty of other languages and ecosystems to choose from.
No one likes small enhancements over an existing language in a new language - it is usually not worth switching over. Remember D? IMO, Kotlin developers have made sensible choices in most cases while retaining full Java interop - something I consider an achievement.
Because C++ and Python aren't safe enough, Haskell is too hard to learn, and Lisp is too flexible to maintain. Or maybe you weigh the factors differently and wouldn't use Java, and would pick one of those languages instead.
> Simple, they all serve a particular purpose. Scala serves a particular purpose, so does Kotlin.
I disagree with this as a language design philosophy. Your language may end up being better suited for some purposes than others, but I find languages that aim to be general-purpose tend to work out better than languages that design for a small niche. Scala in particular was explicitly designed to be a "scalable language" that could be used for both large and small programs - and my experience is that it manages that. (Indeed I'd say it doesn't make sense to adopt it as a language if it's not replacing the majority of your stack - there's more to learn in Scala than in most languages, but if you can replace two languages with Scala then that's still less to learn overall).
> No one likes small enhancements over an existing language in a new language - it is usually not worth switching over. Remember D?
Exactly - I think Kotlin has largely made the same mistake as D or CoffeScript, of not being willing to make a big enough break from the existing language in the space. And yet at the same time we see that it's not so easy for a Java user to pick up, either.
I wonder what langauages are used in aircraft jets, military, space industry, pace makers ... etc
A useful IDE, much faster compile time, lower cognitive load in daily usage.
For all the features it has that Scala doesn't of course, many of which may not be part of the core language (compile times, tooling, backing, simplicity, compile-time null safety, coroutines, quality native compilation, Android support, etc).
And Scala's is good enough, but it's not as good as Kotlin's. Scala has plenty of other beams in its eye to deal with before getting on anybody else, though (this week's shitfire: developers thinking implicits are a good idea, much as it's been for the last five years...).
Nonsense. Putting Option() around any Java-interop calls is no harder than putting explicit "?" types on their return types.
Can you explain how this is so similar to Scala in a way that leaves it up to you to be contemptuous and spit stuff like "nonsense" at people looking to discuss the topic in good faith?
null is not a legitimate value in Scala. There is no reason to ever use it in a legitimate program; in a situation where you would use null in Kotlin, you use None in Scala. The languages are exactly equivalent (except that Kotlin has confusing inconsistent behaviour when you start nesting possibly-absent types, particularly in the presence of generics), people just get confused because they fixate on the literal source string "null" (and Kotlin marketing encourages this confusion). If you really can't stop yourself from typing n-u-l-l for some reason, use wartremover.
> Kotlin also uses annotations that exist in libraries--and it understands a lot of them--to turn T! into T or T? directly when it can, which Scala doesn't.
Those annotations are often unchecked and therefore unreliable, in the cases where they're even present at all (which are unlikely to be the cases where you need to interop with Java - popular mainstream libraries have them, but those are precisely the libraries for which there tends to be a native Kotlin/Scala replacement available). In any case, the original article is talking about the case where there aren't such annotations.
> Can you explain how this is so similar to Scala in a way that leaves it up to you to be contemptuous and spit stuff like "nonsense" at people looking to discuss the topic in good faith?
Sorry. I've seen enough bad faith from Kotlin people on this topic (and when talking about Scala generally) that I find myself unable to assume good faith.
Kotlin is a new language. Several of its deficiencies are a result of primarily targeting the JVM. There are no universal Kotlin "advocates" claiming otherwise, and that is fundamental a distracting strawman.
As to Kotlin -vs- "other language", Kotlin has fantastic, broad tooling (being designed by a very competent tooling company gave it quite a speedy start). In an instant it beats out most competitors that force much more of a compromise.
Such as?
Proper immutable types for e.g. collections. If you're writing in a functional style as Kotlin encourages, you want to be able to write functions that accept lists that are required to be immutable, not just lists that the function isn't going to mutate itself. (Scala's collections get a lot wrong but a thing they get right is that e.g. scala.collection.List is a mutable-or-immutable interface that offers only read methods, and then scala.collection.mutable.List and scala.collection.immutable.List are subtypes for mutable and immutable. Kotlin offers the equivalent of scala.collection.List but not the equivalent of scala.collection.immutable.List)
Better support for operations that need to happen in particular contexts - i.e. custom command-like objects. Kotlin has a bunch of syntaxes that could be reused for this - .?, async/await, and plain old collection iteration all need similar syntax, but there's no way to overload a syntax like that for your custom type. (Scala gets this right: for/yield works for options, for async, for plain old collections and more, but also works for user-defined types).
And ultimately, higher-kinded types. (More concretely: the ability to write utility functions like "traverse" that will work the same for various different context-like types).
Re "command-like" objects: I'm not following, can you give an example of something you can do in Scala here that you can't in Kotlin?
For the same reason that being able to write val rather than var matters. With perfect discipline you can write pure functional code in any language, but in practice (and in codebases that likely have some non-functional sections, particularly if you're interoperating with Java) being able to be 100% sure that a given value is really immutable rather than 99.9% sure makes a huge difference.
> Re "command-like" objects: I'm not following, can you give an example of something you can do in Scala here that you can't in Kotlin?
There are lots of different use cases for this stuff, but one I was working with today was database transactions. I have operations that should only happen inside a database transaction, and want to enforce that at the type level, but I want to be able to compose several of those functions in a single database transaction, so I don't want to just have each one run its own transaction. So what I do is have a custom type, something like MustHappenInTransaction[A], which is actually just an alias for an existing library type (Free), and I automatically get the ability to use the for/yield syntax to compose these, and can use existing library functions to operate on it almost as if it were a plain value, but still safely (e.g. traverse rather than map on a sequence). I have no way to "get the A out" except for my doTransaction() function (what's why I say it's a command-like object), so the function will definitely happen in a transaction, but I can still do normal function composition, extract out common subfunctions, and all that.
You can't do that in Kotlin because there's no equivalent to "do notation" or "for comprehension" that you can use for user-defined types, and there are no higher-kinded types so you can't implement generic helper functions like traverse (there's no way to write the type signature it should have).
class MyTransaction {
fun begin() {}
fun commit() {}
}
fun MyTransaction.query() {}
fun MyTransaction.update() {}
inline fun <R> transaction(tblock: MyTransaction.() -> R): R {
val t = MyTransaction()
t.begin()
try {
return t.tblock()
} finally {
t.commit()
}
}
fun test() {
// update() doesn't compile, no such method
val hello = transaction {
query()
update()
query()
"Hello"
}
println("$hello") // prints "Hello"
}
Does that not work for your usage for some reason? Is there a problem with this approach I'm not seeing? typealias MustBeInTransaction = MyTransaction.() -> Unit
fun makeObject(i: Int): MustBeInTransaction {
return { /* do something with i or whatever in a transaction */ }
}
fun test() {
var genericArray = arrayOf(1, 2, 3).map { makeObject(it) }
//genericArray.forEach { it() } // fails to compile
transaction {
genericArray.forEach { it() }
}
}
Like that? type MustBeInTransaction = implicit MyTransaction => Unit
def makeObject(i: Int): MustBeInTransaction = {
return { /* do something with i or whatever in a transaction */ }
}
def test(): Unit = {
var genericArray = Array(1, 2, 3).map(makeObject)
//genericArray.forEach { _() } // fails to compile
transaction {
genericArray.forEach { _() }
}
}
EDIT - I just noticed that this is almost exactly the same as the example used in the function types link below[1] https://stackoverflow.com/questions/45875491/what-is-a-recei...
[2] https://kotlinlang.org/docs/reference/extensions.html
[3] https://www.scala-lang.org/blog/2016/12/07/implicit-function...
I think this issue with this is what happens when you combine DB transactions with async queries with errors and null handling? It seems like you have to combine 4 different mechanisms in order to do this (receivers, async/await, try/catch, if(null)). Is there some way to do this all with receivers? It seems like they don't really compose.
The 'command' version in Scala could be written roughly the same way for any combination of the 4 features (the types would just change). For example you could write a Scala version of your original example as:
def test(): Unit = {
// update() compiles but doesn't do anything to the DB
val helloDBOperation = for {
_ <- query()
_ <- update()
_ <- query2()
} yield "Hello"
val hello = db.run(helloDBOperation)
println("$hello") // prints "Hello"
}
This would look almost exactly the same no matter which combination of the 4 features you use.EDIT - it looks like I somehow also accidentally replied to you with an earlier version of this comment. Unfortunately , I didn't notice until it was too old to delete or edit. Please ignore the other comment that is very similar to this one. Sorry about the confusion.
https://kotlinlang.org/docs/reference/type-safe-builders.htm...
It's more designed to build DSLs or similar, so you'll find more documentaion/examples in that area of the docs.
> I think this issue with this is what happens when you combine DB transactions with async queries with errors and null handling? It seems like you have to combine 4 different mechanisms in order to do this (receivers, async/await, try/catch, if(null)). Is there some way to do this all with receivers? It seems like they don't really compose.
It's just a closure with a compiler-added paremeter. It can be nullable if you want and exceptions work the same as any other closure. If you want async/await you'd use coroutines. I think coroutines and receivers mix just fine, you'd just make the type a suspending lambda with receiver, but I've never tried.
Thanks for the link. However, I feel like that still doesn't give a lot of detail. Can you have multiple receivers? Can receivers be generic? Can receiver resolution be controlled by other arguments?
> It's just a closure with a compiler-added paremeter. It can be nullable if you want and exceptions work the same as any other closure. If you want async/await you'd use coroutines. I think coroutines and receivers mix just fine, you'd just make the type a suspending lambda with receiver, but I've never tried
Sorry I didn't mean to imply that Kotlin was incapable expressing all of these things together. I meant it would be desirable to express all of these things with the same tools. It seems like the you would have to express these with several different paradigms and be aware of how they interact. That also means you would need more boilerplate because you have to abstract each of these things separately.
I think this issue with this is what happens when you combine DB transactions with async queries with errors and null handling? It seems like you have to combine 4 different mechanisms in order to do this (receivers, async/await, try/catch, if(null)). Is there some way to do this all with receivers? It seems like they don't really compose.
The 'command' version in Scala could be written roughly the same way for any combination of the 4 features (the types would just change). For example you could write your original example as:
def test() {
// update() compiles but doesn't do anything to the DB
val helloDBOperation = for {
result1 <- query()
_ <- update()
result2 <- query2()
} yield "Hello"
println("$hello") // prints "Hello"
} For the same reason that being able to write val rather than var matters. With perfect discipline you can write pure functional code in any language, but [...]
Strongly agree with you there. This is one of the main gripes I've got with many languages. People just have a "well don't do it" attitude and refuse to see the benefit in the possibility of forbidding mutation. A very important point here is the realization that while the part about discipline might be true, it isn't just your discipline the whole codebase might hinge on, either.This is the exact reason why making it easy to do the right thing is a very important part of API design; simply by making hacks more laborious than the sane thing to do, you effectively prevent them most of the time.
So far I've never needed guaranteed immutability, I've just used kotlin's List (which is an immutable view). If that's the only thing anyone gets then it's functionally guaranteed immutable even though it's not sitting on an immutable implementation.
(Kerrigan: Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.)
This is an absurd anti-intellectual non-argument. Saying "pragmatism" isn't an argument; if there were actual use cases where the kotlin design was better that would be one thing, but there aren't; it's just inconsistent for no real reason.
Kotlin seems to have reused the hell out of the poor :
I don't think anyone would deny that Scala has the most features of anything, it's purely a question of importance. Important missing features is more interesting to discuss than purely missing features.
I've found myself more missing features from C++ when working in kotlin - specifically constexpr & static_assert.
What important features are missing?
> The opening lines set the tone for the article
He also wrote:
> we decided to stick with Groovy in /test (Spek isn’t as good as Spock).
If they were going to switch from the legacy language for /main code (i.e. from Java to Kotlin), why didn't they also switch from the legacy language for /test code too (i.e. from Apache Groovy to Kotlin) instead of dismissing it with "Spek isn't as good as Spock". And if they use Gradle, why not switch from Groovy to Kotlin for build scripts also.
Needless to say after my 2nd round with swift it clicked, and then clicked again and again in different ways as the months rolled on. Never looked back.
It seems to me that the author did not spent enough time to learn Kotlin. The way he mixes Java types (Integer.parseInt) inside pure Kotlin code and then complains about lack of Null-Safety? It is enough to use an extension function String.toInt() to stick to Null-Safety and compiler will do the rest.
His main() function? Another weird argument because Kotlin's documentation has some examples on how to write it. Author lacks understanding of how Kotlin is converted to Bytecode.
Complaining about such insignificant things like class literals or variable name shadowing (there is a compiler warning for that!) makes me want to shout: Hey, Kotlin is the most amazing language ever if those issues ended up as #1 and #4 on authors "bad bad Kotlin" list...
Generally speaking, when someone writes about a programming language in a bit hateful way it is better to learn the language first.
For example, the null safety example and the complaint that having `!`, `?`, `!!` is "too Scala like" and too complex. I wonder if the author ever had to reason about <? super T> or <? extends T> in Java (e.g. https://briangordon.github.io/2014/09/covariance-and-contrav...). Scala doesn't even have these sigils (there is a different syntax for variance, type bounds and context bounds - but that's about all there is).
Another example - "In the C-family of programming languages, we have the standard way of declaring types of things." At that point you may as well point out that maybe Kotlin isn't in the C family.
The type bounds in Java are there for a reason. Kotlin's syntax is simpler but it it unable to express all the types that the Java model can.
I have found myself in situations where I want unable to express the proper type information in Kotlin simply because the designers of the language decided that having full support was too complicated.
List<? super T> is List[_ >: T]
List<? extends T> is List[_ <: T]
Scala leave a feature out? Psht...always room for one more.
Of course, that's the price you pay for compatibility with Java, but maybe there should be tooling which can give warnings where you are using commonly abused Java tools where you'd be better off using Kotlin-specific ones.
There's really nothing in the article that warrants any of this.
As to the author of the thing, they just wrote about not liking Kotlin, you're the one who is taking this as some sort of personal affront. How do you know how long they spent learning it? You don't.
Safely calling option.map and having the function assume it's not called with null is very useful. Is that a real drawback to Kotlin or is there something missing from this post?
public int parseAndInc(String number) {
return Optional.ofNullable(number)
.map(Integer::parseInt)
.map(it -> it + 1)
.orElse(0);
}
And the Kotlin equivalent..."No problem one might say, in Kotlin, for mapping you can use the let function:
fun parseAndInc(number: String?): Int {
return number.let { Integer.parseInt(it) }
.let { it -> it + 1 } ?: 0
}
Can you? Yes, but it’s not that simple. The above code is wrong and throws NPE from parseInt()."Yes - that's why Kotlin standard library provides, among many others, a convenient extension function String.toInt(), which is a wrapper around Integer.parseInt().
You're supposed to use that, and then you're safe from NPE, because the receiver has to be explicitly non-nullable (it's String.toInt(), not String?.toInt()).
Also there is no need to redundantly repeat "it" inside the lambda - plus you could further simplify the implementation by using type inference and converting it to expression body:
fun parseAndInc(number: String?) = number
?.toInt()
?.let { it + 1 }
?: 0
"Now, compare readability of the Java and Kotlin versions. Which one do you prefer?"Well, I'll say orElse(0) is more readable than "?: 0" dangling at the end of the expression. However Java is more verbose. I'd say it's a matter of taste.
The difference is that thanks to Kotlin's features - which allow for creating custom DSL easily - implementing orElse is trivial, if you can't live without it:
fun Int?.orElse(fallback: Int) = this ?: fallback
This allows for: fun parseAndInc(number: String?): Int = number
?.toInt()
?.let { it + 1 }
.orElse(0)
And this, to me, is already nicer than your Java version. fun parseAndInc(number: String?) =
if (number != null) number.toInt() + 1 else 0 fun parseAndInc(number: String?) = (number ?: "-1").toInt() + 1
;) fun parseAndInc(number: String?) = number?.toInt()?.inc() ?: 0Wrapper types currently incur overhead in Java. If/when value types are finally implemented, this overhead will become negligible. In languages such as Rust and C++, Optional is already a zero-cost abstraction.
From a type theoretic point of view it's clear that Optional has a monad structure, and all the usual combinators make perfect sense for it and are actually a joy to use in languages that support them, including Java. After all, it's perfectly isomorphic to a list with either 0 or 1 elements.
Or even better:
fun <T> T?.orElse(fallback: T) = this ?: fallbackI agree that the null-safety Java interoperability does not work properly if the Java code is not annotated correctly (or you're parsing JSON) but I'd still prefer to have it than not.
I like Kotlin because: * If is an expression and it feels so clean * Functions are first-class (you can pass functions to functions) * Kotlin is less verbose than Java so there is less to read and write * You don't have to write "new" * Constructors are cleaner (you don't have to write (this.a = a) * The map function (on Android we were stuck on Java 7)
On the other hand, Java 8 classes like java.util.Stream only exist on API 24+ devices. So if you want to support older classes, you can't use the standard stream library.
See a talk by Brian Goetz - https://www.youtube.com/watch?v=MLksirK9nnE
Not only C++; C# also. It's not very hard to confuse the author, is it :) I understand it's different from Java he's used to, but that doesn't mean one can hold every such difference against Kotlin. Not being Java isn't in and of itself a fault of a language.
Why is ":" problematic exactly? It's more concise. It's true that this gives ":" more than one meaning, but it's so clearly context-dependent I can't really see how it could cause confusion when coding. An example would help.
(Ruby uses "<" to denote class inheritance - by some miracle Ruby devs manage to get over the fact that "<" is also an arithmetic operator :) )
Read the colon as “is a”, and it makes sense.
“counter is a number”
“DogClass is a (special) MammalClass”.
Not seeing a problem there, but again, I have no particular love for curly brace languages. And I would MUCH rather see the type expression spewage AFTER the symbol name - especially given Java 5 generic declarations mixed with Java 8 lambda declarations (yuck!).
Anyone, including non-java programmers, can make an educated guess at what the "extends" keyword does to a java class.
The colon operator is contextless. Unless someone tells you or you've coded in c++/c#, you can't know for sure what it means without googling it.
Here's a case in point, my biggest bugbear with the nim documentation. When you first see nim code, you're gonna see the '@' operator pretty much immediately. You're going to wonder what it does, and google "nim @ operator". You'll then remember that googling symbols doesn't work very well, and google "nim at-symbol operator". Sooner or later, you'll find your way here:
https://nim-lang.org/docs/manual.html#lexical-analysis-opera...
The nim documentation helpfully says "Yes, the @ symbol is an operator".
So what does the @ operator do? It's a mystery, go fuck yourself, errr I mean figure it out for yourself!
If you ignore the mystery and move on with your learning you'll come back to it via the key word "seq" within about 10 minutes, but there's no easy way to search for it if you don't know that, and by the way what a frivolous waste of time.
Shorthand symbols are worth having, but they should be used only in cases where the coding speed benefit outweighs the readability loss. If you had to write "extends" in kotlin instead of use a colon, how often would you be doing it? If it's more infrequent than once every 2 minutes I would argue that "extends" is better because it's in natural language, and thus easier on average for new kotlin developers to understand /regardless of background/.
Certainly more often than you'll need someone to tell you what ":" in a class declaration does ;)
I see your point, I just don't think this language trait deserves to rank as "confusing" for a professional programmer.
While we're at it, Kotlin, unlike Java, provides explicit "constructor" and "init" keywords. So if it loses points for the lack of "extends", why doesn't Java for not having these?
Nim's manual doesn't seem helpful indeed (they acknowledge it openly in the beginning), but I feel this is sort of beyond the point.
Kotlin's docs are very clear and exhaustive. Case in point - as we discussed inheritance declarations - https://kotlinlang.org/docs/reference/classes.html
No one said it didn't :)
In general, I personally prefer my languages explicit for a variety of reasons. There's a balance for sure, and one can go too far the other way. For example, I find Rust to be a shade too unwieldy for my optimum preferences, although I would overlook that to gain the safety benefits if I had the right use case.
It has a type after. Always. Even assuming that's not enough for it to click, that Google search took all of 3 seconds and you are now typing ":" over "extends" thousands upon thousands of times. If you honestly thing one of those takes more time than the other, you haven't really thought this through.
This is a silly argument.
That's blatant hyperbole, and also a worthless argument. How many times have you typed "int"? Should we replace it with a symbol? Maybe go back to space-cadet keyboards[1]?
J is a language built around symbols for maximum programming speed. It's not an esolang, it's real and usable, and recently updated. Here's a very simple J function:
result=: {&((65 97+/~i.2 13) |.@[} i.256)&.(a.&i.)
Very efficient, minimum keystrokes. Can you tell me what it does?
Fundamentally, programming languages are for people, not computers. Otherwise, we'd all be coding in assembly. Keystrokes are /not/ the expensive or dangerous part of programming, we shouldn't be optimising our languages to reduce them. Optimising languages to reduce miscomprehension is the future, that's where expensive developer time and business risk is concentrated.
Preferring to type a colon instead of the word "extends" is, bluntly, the sort of thing you solve with a custom autohotkeys file.
In any case, it's a minimal gain either way with the : operator, that kind of decision won't make or break any language. But this is the internet and if we're going to argue over something so trivial, I'm going to argue that on the most nitpicky level, "extends" is clearly a better choince than ":".
[1] Space-cadet keyboards are really cool
Very efficient, minimum keystrokes. Can you tell me what it does?"
Well; can you tell me what this means? :) https://www.publicdomainpictures.net/pictures/20000/velka/si...
Obviously there are extremes on both sides of this spectrum... It's subjective where the perfect compromise between being explicit and being concise lies. Some languages value the former more (like Pascal or Python...), others are more terse. It's always a trade-off.
"Fundamentally, programming languages are for people, not computers"
Of course, but that doesn't mean the correlation here is as simple as "the more verbose, the better". Otherwise than you imply, verbosity does have its own inherent cost too. It increases the cognitive load on the reader, who now needs to process more text to the same effect, as the meaning-to-text ratio drops.
"Keystrokes are /not/ the expensive or dangerous part of programming, we shouldn't be optimising our languages to reduce them"
It's not about keystrokes though, since code is read much more often than written. Every piece of code written once will be read several times eventually.
So would you prefer eg. begin..end to curly braces? Would you prefer "AND" to "&"? Even wordy language such as Java have been heavily optimized to save space.
It's obviously subjective, but the programming community "votes with their feet", and if you look eg. at the "most loved languages" StackOverflow poll - https://insights.stackoverflow.com/survey/2018/#technology-m... - it doesn't make an impression like wordiness is what people are after in a programming language.
And on the other direction, the bytecode generated by the Kotlin compiler also has these Java nullability annotations (it uses the org.jetbrains annotations package), so for instance Dagger2 can know that a type is nullable from the presence of the @Nullable annotation.
If your code already uses @NotNull/@Nullable everywhere, introducing Kotlin to your code base becomes easier. As a bonus, IDEs can sometimes use these to warn you of potential null pointer mistakes in your code.
I don't mind them doing this either. I just find the thinking outmoded, and I also don't mind just not using Kotlin. And to clarify, it's not just the decision to not support language server protocol at this time; it's their reaction to the mere suggestion of it. The language steering is too coupled to the biz IMHO. For what it's worth, I feel the same way about F# and the Visual F# Power Tools team and is the prior experience that raises the red flag.
It is a shame some people are disinterested in helping out. The Kotlin team can only do so much on their own...
Everyone is entitled to their opponion. But, this article doesn't seem to have much of a point. It would be like if I wrote and article about how I preferred cherry popsicles to grape popsicles. That my opponion and I'm entitled to it. But, I don't know why I'd want to be publish a blog post about it or why anyone else should read it or care.
Consult with your CEO, CTO and/or Director of Engineering before switching to Kotlin, there may be things you might not be aware of, think 20 years ahead and think objectively about the pros/cons before heeding to advice of the enthusiasts, they won't be there to help you when your app behaves unexpectedly, or if your tech debt increases or when you are unable to find people willing to work with your messed up code base, remember there's no reverse Kotlin to Java converter built into IntelliJ.
My advice is to sit it out and wait for Java to evolve, which shouldn't be far away, in the meanwhile enjoy writing your code in Java in which you have your actual work experience(8+ years in my case), which has books and resources dedicated to help you understand the pitfalls, design patterns and every trick out there since 22+ years of its existence. Besides I would recommend developers to not waste their time on a language which piggy backs on JVM, many other languages which did that or are doing it have failed, Kotlin won't be an exception. Instead use Java to learn and write complicated Data Structures and Algorithms and learn technologies like Machine Learning, Neural Networks and AI etc to increase your job prospects.
Now back to this blog post and enthusiastic defence of Kotlin:
This blog post wasn't a criticism, it was instead under-informed and misleading. You will indeed attract the attention of enthusiasts if you take an authoratative viewpoint against the thing those enthusiasts care about, publish it publically, promote it to the whole community, and use bad (or no) evidence in the process. Who wouldn't stand up and protect something they care about in that circumstance?
By the way, when we were helping to create Java at Borland, we were told many things like you just said: "it will fail", "the CEO/CTO/CIO won't want it", "we can't take the risk", "in 20 years it will be gone", "it's just a toy and not for the enterprise", "dancing Duke is all it can do", "it will fail like the others, Java is no exception", "no real application will be written in it", "it can't do server-side", "stick with C++", "stick with Delphi", "stick with VBA", "stick with PHP"
So obviously the prognosticators and non-enthusiasts were wrong. What makes you better are reading the future than they were? Is Google wrong when backing Kotlin? Is JetBrains? Is Square? Is the Spring Team? Is the Gradle team? Is the JUNIT team? Or are these some of the same people who made the right call on Java way back in 1995-2000 as well? In fact Java would have failed had the enthusiasts not carried it through tough times, shaped it up, improved performance, fixed critical bugs, cleaned up bad specifications, and showcased it to the world. Because of enthusiasts, you have Java.
So let the enthusiasts do their work of defending a good thing against people that just don't yet see the light (or maybe never will care to, so bet it).
T! being a little odd is a fair criticism--or would be if it wasn't literally inescapable when working with languages that are, like Java, guilty of Hoare's mistake. For my money they do a better job of it than Scala does, that they address it at all when Java doesn't is admirable, and I think it generally works pretty well. I think Kotlin should make every T! a T? and force you to deal with the null cases, to be honest, but that would probably make the author upset because it'd splat `!!` all over the place.
Companion objects are objects, not special-cased static...things, and are significantly more useful than static methods and fields once you have put in a few seconds of chin-stroking to consider why people as smart as the Kotlin crew would have gone that way. Making useful things out of classes in Java sucks, whereas in other environments (something like Ruby comes to mind--as an example, Sidekiq::Worker provides meta-information to Sidekiq because of the configuration of your job class) it's fluid and natural. Kotlin tries--it's not perfect, but within the bounds of playing-nice-with-Java it tries--to improve that. Treating companion objects as useful things in Kotlin is solid.
And I have no problem writing a program entrypoint from memory, on the rare occasion I have to. Probably because I understand what everything in it actually...does? And I'm not a Kotlin specialist or anything, I just take the time to understand my tools when I use them.
Option/Maybe types are a good idea. I've used Kotlin-specific ones in Kotlin and it was...fine, but the "oh, look at this `let` thing, isn't it so much harder than the Java one?" thing is a head-scratcher. It smells like another case of "I expect things to look like Java so I throw a rod when it doesn't".
Data classes are great. That C# doesn't have them is one of the reasons I dunk on the language so much (along with the `static` mistake, but like Java, they were young and foolish). Closed-by-default classes--meh, whatever. Have that argument if you care that much, but we're a tool-using species, set your linter to complain about closed classes if you really care that much.
Don't get me wrong: I think one is probably wasting one's time to be using Java preferentially in 2018, but use what makes sense to you. Have decent reasons for it, though, and a lot of this seems like "this is different and therefore scary and therefore bad." Like--steep learning curve? Man, I was writing mostly-idiomatic Kotlin that plugged right into Java libraries in a couple hours.
This whole article reads like a weird lazy lament: that changes are hard, seeking new local maxima of correctness and productivity is uncomfortable sometimes, and challenging that discomfort isn't awesome. And maybe Kotlin is worse for the author's particular flavor of code--but, having written a lot of Java and a lot of Kotlin, I really doubt it. Kotlin is spectacular for me specifically because it does not force me into Java's lowest-common-denominator design. It gives me room to be smarter about the stuff I write while still using the often-very-good libraries available through Java on the JVM. I don't get to write as much of it as I'd like, these days...but doing so is consistently fantastic.
EDIT: Actually, while I'm at it, I'll give my one real grinds-my-gears thing with Kotlin: I think the way they do blocks is odd. I think that the braces should encapsulate the body of the block, not the variable expression as well; I get the resemblance to Ruby but I think their choice of `->` makes it read oddly. But that's a minor nit compared to the real one: `it` is a wart. I think you should have to name your enclosed variables and I don't think you should be able to shadow them. Heck, the reason I read this article in its entirety in the first place is because the author's first point about name shadowing is a good one...
If you know Java, you can be start writing kotlin after a couple of hours with the kotlin koans.
My main pain point with kotlin is that I generally have a good idea of how my code will look like as bytecode (like this feature will need an intermediate object, so let's use this instead in this very big loop). In kotlin, it is a bit more of an uncharted territory, even with the fantastic 'see bytecode' tool.
Especially with one that is constantly evolving like kotlinc.
Some of the bad practices of 5 years ago have devolved into cargo cults and the compiler can actually handle these cases fine now.
So I guess that this is not that great of a reason to choose either Java or Kotlin.
In both cases, if performances are critical and lacking, you will have to whip out systrace and look at the bytecode.
I can't find an example of what I imagine you're saying.
Can you link to an example from here the reference docs?
// Lambdas are code blocks enclosed in curly braces.
items.fold(0, {
// When a lambda has parameters, they go first, followed by '->'
acc: Int, i: Int ->
print("acc = $acc, i = $i, ")
val result = acc + i
println("result = $result")
// The last expression in a lambda is considered the return value:
result
})Name-Shadowing is confusing? Should be common sense to not have multiple variables with the same name in the same scope.
Optionals are ugly in Java-Interop, so we decided to just use Java instead. Great reasoning.
You're saying name-shadowing isn't confusing because it should be common sense not to use it? Then why not just enforce that in the language?
Then I realised it's a different Allegro.
Another case where knowing Lisp makes you a better programmer, no matter the language you use.
Below, the comment he's deleted twice; you be the judge :) (I wouldn't normally repost, but the first time round I erroneously assumed it was due to including a link in it; apparently not).
----
The article has been commented extensively on Hacker News. I'm not pasting the link, as this gets my comment marked as "spam" (go figure), but it's trivial to find it with very basic google-fu.
Now, while I don't approve of the unneccessarily biting tone of some of the remarks posted there, I think it's been demonstrated convincingly that a large share of this Kotlin critique is highly dubious at least.
I believe that in general the longer we work, the less eager we are to learn new stuff, and we become more set in our old ways. It's only natural and human, but often we go to great trouble only to rationalize this attitude somehow. And that's what should set your alarm bells ringing. For the sake of your own development, nobody else's.
Publicly criticizing a language without even knowing its standard library (as evidenced by the "Integer.tryParse" example, which indeed runs into troubles, but only because it's outright wrong - see the comments on Hacker News for more details) seems premature, especially for a veteran programmer like the author.
----
To me there are two takeways here; more important than Kotlin. Two factors that inhibit our professional development, especially in the long run, once we're "seniors" already.
The first is what I already pointed out in the blacklisted comment, above.
The other is sort of self-evident now; and that's the ego problem. Not only we become conservative over time; we also develop an oversensitive ego, and conversely, an allergy to being corrected. I really hope I won't fall into this trap myself... I sure as hell exhibit similar signs now and then
Colon between names and types (as in "i: Int") "makes work in Kotlin harder"? Seriously? By as much as having to end lines with semicolons in Java? : ) I understand it's a matter of habit, but how could that possibly make anybody's work hard is beyond me
"I can’t imagine a valid use case for shadowing a method argument."
Arguably this would be better off causing a build-time error, like in Java. But is this such a problem in practice? Do you really keep bumping into it accidentally? Just don't use name shadowing...
"In my opinion, Kotlin’s type system with all these scala-like !, ?, and !! is too complex."
You've listed "all these" already :) All three of them, among which "!" isn't even something you'll ever use yourself. How is it more complex than explicit null checks?
It's also trivial to implement Optional in Kotlin if you think you need it. Thats pretty much all you need: https://github.com/gojuno/koptional/blob/master/koptional/sr...
"Why Kotlin infers from Java T to T! and not to T??"
Probably because not doing that would require putting "!!" or "?" pretty much everywhere you call Java code, even code that you know for certainty won't ever return null - such as Observable.create or every single RxJava operator, for instance.
Observable
.fromCallable(something)!!
.map(String::trim)!!
.distinctUntilChanged()!!
.switchMap({ searchPhrase -> handleSearch(searchPhrase, view))}!!
.scan(
initialState ?: DEFAULT_BLANK_STATE,
{
full, partial -> full + partial
})!!
.doOnNext { logDebug(here) { "Returning new, updated full state:\n$it" } }!!
Etc. That's what it would have to look like if they made it your way."In Java, we write the class name with .class suffix:
Gson gson = new GsonBuilder().registerTypeAdapter(LocalDate.class, new LocalDateAdapter()).create();
[...] in Kotlin, you are forced to write: val gson = GsonBuilder().registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()).create()
Which is ugly."Seriously? :) It's pretty much identical code, with slightly less noise in Kotlin. All the difference is type inference (which you praise at the beginning), and no need for two "new" keywords in Kotlin, at the cost of having to add one ".java" after "::class". The difference is rather subtle, so I honestly fail to see why the former version passes for normal while Kotlin's is suddenly "ugly"...
inline fun <reified T> GsonBuilder.registerTypeAdapter(adapter: Any) = this.registerTypeAdapter(T::class.java, adapter)
Bam, now it'd be: val gson = GsonBuilder().registerTypeAdapter<LocalDate>(LocalDateAdapter()).create()val map = mapOf("firstName" to "John", "lastName" to "Doe")
The above seems a really strange decision for a "new" language - why would you not just use the most common structure, being JSON?
So you could trivially do your own listOf or mapOf that creates whatever data structure you want, whereas languages with special syntax for this tend to hardcode the result of it. That's a valid choice, sure, but just that there are tradeoffs here rather than it being obviously better one way or the other.
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/...
I've been told that it's because "how would it know which specific sequence and map class you want", but it already has Seq() and Map() which give you the default immutable sequence and map classes, so obviously a literal syntax would give you the same one.
i also heartily agree with the article's points about nullable types and Java library interop: it's pretty annoying to deal with Kotlin non-nullable types that have to interoperate with existing Java libraries that have nullable types in return values or function callback parameters. i have way more question marks in my Kotlin code than i originally thought i would.
i also appreciated the article's scrutiny of the name and type order reversal in Kotlin and the "::" syntax for getting the class literal (::class vs ::class.java). still trying to get used to that.
and as for the learning curve, i have to admit that it's been steeper than i expected when i first saw introductory presentations. Kotlin feels very abbreviated. the smart type inference and things like 'lateinit' have sometimes surprised me in their behavior. these aspects of Kotlin are indeed very smart, sometimes too smart for me. i feel like i have to think harder to understand a piece of Kotlin code, which is ok, but somehow, i wasn't expecting that based upon the initial presentations.
overall, Kotlin is ok i guess, but, to go off-topic for a sec, as an Android programmer i wondered why Google chose to emphasize adding a new language when many other aspects (e.g. documentation, the jumble of GPS, Firebase, GCM and 3rd party libs, mysterious adb failures, emulator problems, instant run, etc), of the ecosystem needed improvement/simplification/clarification more desperately.
I disagree about the name shadowing issue. In fact, I wish Kotlin didn't suck so hard and not allow me to ignore those warnings specifically. My primary use for name shadowing is so that I don't use the previous variable. This may be a bit strange, but in highly functional contexts with immutable variables, shadowing a name is reasonable especially as you begin to nest. It's not like Kotlin has a problem reusing/shadowing the "it" var name in nested blocks. Rust makes me very happy with how it handles it and it makes the code quite readable. Something like `val someValue = someValue ?: error("Bad value")` is very reasonable. If anything, you can have reasonable scoping warnings if there appears to be ambiguity, but it should not be across lambda blocks as it is now.
Can't cite type inference in Java if one of its most popular platforms doesn't support it. May be ok for you, not ok for everyone though. We might wish Android would keep up, but in the meantime we have practicality concerns.
I don't think those three nullability sigils make it too complex and has nothing to do with Scala where devs can define their own sigil operators. And Kotlin is ok about inference when the Java code has reasonable nullable/not-nullable annotations. There are a lot of problems with Kotlin's compile-time null checks (namely their insistence on thread safety so a null-checked field can't be reaccessed and assumed null), but "platform types" is the best that can be done within the constraints of existing JVM code.
Having the IDE put ::class.java in there is hardly a reason to go away. That's like saying I'm going back to Kotlin because sometimes in Java I have to type .class if its a type and .getClass() if its a value. The obvious reason for it is that Kotlin classes are supersets of Java ones and have more reflective properties. That and not everything is about the JVM (Kotlin has two other targets). If you can use KClass's, they are preferred.
There are too many articles on why languages like Scala, Go, Rust, etc choose to add the type to the right hand side of parameters/functions for me to re-explain it here.
Your complaints about statics are because how the JVM handles statics, not Kotlin. I appreciate the object approach over JVM statics. Just sucks that you keep having to go back to JVM-land where you are hamstrung. As your apps and library usage grows, you will move further away from the requirements forced upon you by Java and stop complaining about that being the reason you are going back.
The key-value not using a new operator thing is simply because one is defined as a piece of a library and not part of the language. You want them to write a special operator for maps which are not even part of the language (just a class that happens to be part of the stdlib)? Instead of an infix "to" that creates a Pair class? The disappointing part is wanting to extend the language syntax even further.
You wrote:
fun parseAndInc(number: String?): Int {
return number?.let { Integer.parseInt(it) }
?.let { it -> it + 1 } ?: 0
}
and complained about readability, when I see that and think:
fun parseAndInc(number: String?) = number?.toInt()?.inc() ?: 0
And I think, not only is that quite readable, but it sure isn't very practical to accept a null number here. Take a peek at the generated bytecode at some point too compared with your Java version. I'm a little concerned that this kind of stuff has you switching languages...if you make bad examples I bet they'll look bad.In your back-to-Java article you mention data classes with no Java alternative.
Spring is annoying. If you're a Spring shop, and you're ok w/ all the magic, stay in Java. It might be best.
Sorry I typed so much. I have lots of problems with Kotlin too...but I sure don't share share the ones in the article.
Agree about the shadowing.
var x_2 = f(x_1) // never use x_1 again for anything
deserves its own syntax and compiler checksNobody forces everything to be thread safe. That would either be suicidally complex or suicidally slow. The problem isn't so much "ignoring thread safety", it's that this:
class Foo(var thing: Int?) {
fun doSomething() {
if (thing != null) {
thing++
}
}
}
doesn't compile and it should. It fails to compile claiming that 'thing' could have changed in the meantime, so it can't safely assume non-null. However, that can only happen if Foo is accessed from multiple threads, but this isn't thread safe nor pretending to be in the first place. You can make the compiler happy by doing this: class Foo(var thing: Int?) {
fun doSomething() {
val t = thing
if (t != null) {
thing = t + 1
}
}
}
But of course that's not remotely thread safe, either. It wasn't thread safe to begin with, and it's still not thread safe now. But this will of course compile just fine. You're forced to jump through hoops to workaround compiler "bugs"To be fair here the warning isn't actually about thread safety at all, it's to guard against something like this:
class Foo(var thing: Int?) {
fun otherThing() {
thing = null
}
fun doSomething() {
if (thing != null) {
otherThing()
thing++
}
}
}
Kotlin has so far just been unwilling to allow the compiler to handle the cases where it could prove that the variable hasn't been modified in the meantime because they can't always prove it. class Foo(var thing: Int?) {
fun doSomething() {
if (thing != null) {
thing++
}
}
}
is unsafe as any class user could cons up a Foo t, and call t.doSomething() while racing a modification to t.thing in another thread. There either needs to be a language feature for Foo to live in a restricted threading context or the whole construct is irreparably thread-unsafe. The error is valid because it rejects always-incorrect code.There is no possibility of any kind that my snippet is null-unsafe exclusively. My code is null-safe in all situations where the resulting behavior is also correct. And the compiler could trivially prove that.
If they wanted to actually take a stab at compiler-audited thread safety they should add some annotations or such to mark what is guarded by what. Otherwise the only reasonable assumption is to assume thread-compatible. Which my code also runs correctly in.
Google and their J++.
I don't see myself writing full time in java again, ever.
/me hopes he didn't completely butcher the Spanish.
See for me it's more natural to think of it as Eric is a Person, rather than Person named Eric.
Also, if your type is something stupidly long (ex. Java) its really easy to read something like MyAbstractFactoryBeanImplBuilder while you have a bunch of stuff in your head and space out/forget something. Humans aren't good at keeping a bunch of things in their heads, so I'd much rather just have the names of these things and then types be as a reference to return to as a "oh wait, what was this thing again?" sort of deal.
As the classic "A Brief, Incomplete, and Mostly Wrong History of Programming Languages" (https://james-iry.blogspot.com.br/2009/05/brief-incomplete-a...) would say:
This criticism happens in spite of the fact that C has not yet been invented.
Others have mentioned the Pascal / Modula / Eiffel type languages, I’m sure, as well as even newer languages that do type inference.
I wanna see the name of the thing on the left margin, rather than AbstractBoilerPlateFactoryFactoryFactory. Or maybe, I don’t even wanna see the thing at all, and just evaluate an expression as a parameter to a function :-)
What do they bring on top of static fields and methods ?
What this author seems to get hung up on is that not all Java idioms port well to Kotlin. Some of his criticism is fair, e.g. the companion object thing is indeed a bit of a kludge. No matter how the Kotlin people spin it, the fact is that it is coming up frequently in forums, stackoverflow, etc. It's clearly a problem and it is entirely fixable.
So, it would be helpful if they added proper support for static fields and methods without requiring ugly annotations and offer full back and forward compatibility to Java. The JVM supports it and clearly their compiler supports it if you get insistent with the right amount of ugly annotations and verbosity. So, I see no sound technical reason for not doing this. However, it is not a show stopper. Companion object with some functions, add @JvmStatic annotations, problem solved.
There definitely are some more ugly corners in the language but mostly is clearly better than Java. And just because it's there doesn't have to mean you have to use all of it all the time. Adding question marks all over the place doesn't look like idiomatic Kotlin to me. Better to just have null safe code and not having to deal with nulls. Also, java.lang.Optional works just fine in Kotlin and kotlin probably adds some useful extension methods.
Otherwise, sane generics, extension functions, flexible property definition, data classes, no difference between boxed/unboxed primitive types, etc. All great stuff and kind of refreshing after dealing with Java's broken type system for years.
Add to that the upcoming support for co-routines, javascript (you can do react apps in kotlin) and native compilation, and the endorsement for android development, awesome tool support, etc. and you have a great language that is ready for just about anything.
So, mostly good stuff to say about Kotlin. As for Scala/Groovy, I never really cared for either. I'd say Kotlin's strength is that it cherry picks a few popular things from those and other languages without really trying to be those languages. It's seems to be succeeding where those languages failed in trying to get Java developers to abandon Java. Java 10 is a nice incremental change but doesn't really address most of the things that Kotlin fixes.
1. Name shadowing is a compiler warning. It is good to pay attention and clear all warnings by resolving the issues. It isn't a secret when you name shadow, and a developer I would hope would pay enough attention to not do it on accident. I've used Kotlin since 2013 and don't think I've shadowed a name even one time on accident. There is an issue in the Kotlin tracker to allow turning specific warnings into errors, this would give you the behaviour you want once added.
2. Kotlin type inference is the best out there, nothing compares in accuracy and scope. You aren't getting the same thing from Java 10 that Kotlin has by far.
3. Compile time null safety is accurate from Java libraries if they inform as to their null safety, for example there are many libraries that use `@NotNull ` and `@Nullable ` annotations and Kotlin respects and enforces those. They also handle this for JDK calls so that all standard library calls are correctly interpreted. You can fix your own custom Java libraries which also helps with Java static analysis. And eventually you'll use more and more calls to your own Kotlin code or to Kotlin libraries and you'll really be glad all of this null safety is there and part of the compiler.
4. Class literals are many times not used in Kotlin API design. Instead you as an API designer would just write an extension function that reifies the generic parameter and auto infers the class literal. Or you can do it yourself to extend any API you wish. You will only do that once and then enjoy the magic a thousand times after. There are actually modules for things like Jackson and GSON that do this already. So then the syntax is better than the Java because you don't have that parameter at all (which is type erased whereas reifying the full generic type is not). Here is an example of Java vs. Kotlin using Jackson object mapper in the same use case you were demonstrating:
Java:
final MyStateObject state = mapper.readValue(json, MyStateObject.class) // type erased, which sometimes is ok
final List<MyStateObject> states = mapper.readValue(json, new TypeReference<List<MyStateObject>>()); // not type erased
Kotlin: val state: MyStateObject = mapper.readValue(json) // not type erased
val states: List<MyStateObject> = mapper.readValue(json) // not type erased
Alternative Kotlin: val state = mapper.readValue<MyStateObject>(json) // not type erased
val states = mapper.readValue<List<MyStateObject>>(json) // not type erased
Isn't type inference wonderful? Especially with reified types making API's like this possible.5. Type declarations in the form of `name: Type ` are by design to allow for unambiguous syntax which keeps the language smaller and leaner than the C style typing which cannot be used everywhere without extra cruft to delimit what is going on. Plus this is more readable when scanning variable declarations since they are at a fixed position from the left instead of a random position on the right. Here are some good reading for you that let you know the Java style `Type varName ` is actually the less common across languages: https://softwareengineering.stackexchange.com/a/311739/20888... and then this post about how it interferes with higher level concepts in language to have it backwards as Java does: https://softwareengineering.stackexchange.com/a/311730/20888...
6. Companion objects are there so they can be extended, including with extension functions. You can easily do things like logging using other models and delegates instead of adding a companion object. Your logger system likely caches the logger by the way so having a reference on the object is not a huge item to add. You can also have a `main()` as a top level function and is not required to be in a class which probably makes more sense for a main anyway.
7. Collection literals are not present in Kotlin because in the JVM developers are more specific about the exact type of collection they wish to construct, and there are simple helper functions which make things clearer. The `: ` being used for maps would overuse that symbol and cause it to have different meanings (I think you complained about two uses of `: ` then you later suggest a third). What is wrong with `to ` for creating a pair which is used to create the map? it is an extension function, highly readable, and you can actually make your own with some other name (other than `: `) `to ` isn't part of the language syntax nor forced upon you. The Groovy syntax is bad because it looks like you are declaring a list/array. The other syntaxes don't say whether the maps are readonly or mutable and that is important in Kotlin. It is a carefully thought through issue.
8. Maybe? is not needed as much due to alternatives and null safety. But, there is the JDK Optional which you are showing and can just as easily be used by Kotlin as well. And more "native" versions in open-source Kotlin libraries. Kotlin just doesn't have one in its standard library, nor does it need one. I personally dislike API designs that force optionals on me for everything, so I'm glad Kotlin doesn't.
For your example code, why are you using Optional for this case of `parseAndInc `? Kotlin has the `?. ` safe operator to continue a chain when not null, and the `?: ` Elvis operator to provide a fallback on null values. Any operator like `+ ` is also available from its operator function (i.e. `plus() `) or in this case simply `inc() `. Therefore:
Kotlin:
fun parseAndInc(possibleNumber: String?) = possibleNumber?.toIntOrNull()?.inc() ?: 0
Yet you can also write the same Kotlin code as your Java example (and slightly less typing):Also Kotlin:
fun parseAndInc(number: String): Int {
return Optional.ofNullable(number)
.map(Integer::parseInt)
.map { it + 1 }
.orElse(0)
}
Compared to your Java: public int parseAndInc(String number) {
return Optional.ofNullable(number)
.map(Integer::parseInt)
.map(it -> it + 1)
.orElse(0);
}
Please remember that everything you can do in Java you can do in Kotlin including using all those Java classes you are familiar with. But, you can also learn idiomatic Kotlin and write it differently. This is one of the reasons the learning curve is very low.9. Data classes, so you aren't complaining here or are you? You can't inherit the equivalent in Java either, you would have to rewrite `equals `, `hashCode `, `toString ` and more if you did that and each would need to be customized. So Kotlin has the same issue, inheritance of these is dangerous because manual decisions need to be made in order to do so. You CAN inherit from another class, and you could use composition and interfaces instead. Data classes are a life saver in code, covering a huge number of cases without all the boilerplate of manually coded JavaBeans.
10. Final classes by default are a good thing, and this has been heavily researched and talked about in the Java community as a best practice. Some old Java libraries do not know how to handle this because they don't expect it to be a problem. But those libraries are changing, and complier plugins for these libraries already help you out with 0 effort in changing code. Plus, newer libraries deal with this without problems. If you want to modernize, you have to modernize and not just expect new things to continue with allowing bad habits because some legacy code doesn't do things elegantly. I can't think of any blocking case related to this. It was well known, well covered, and is a non-issue.
11. Shallow learning curve. Kotlin is small language, consistent, and elegant. It does NOT have a steep learning curve. It is no where near Scala in terms of effort and complexity, yet you lump it in there anyway. Plus, isn't it worth learning a language over a week that'll save you 40-50% the lines of code, automatically eliminate whole categories of bugs you write, be many times more readable and maintainable? Kotlin is a few days to a week for an ex Java developer to write reasonable code, and then later they'll write more elegant code over time.
And about Spock vs. Spek: You don't have to pick either/or, since you can use Spock from Kotin too like any other Java library.
time kotlinc Hi.kt user 4.5s !! (OK IN IDE on second run)
trailing comma not allowed
no python like package manager. import mail? no
a = b = 1 // assignments are not expressions
The authors wanted to name it after a Russian island (as a nod to Java); if you browse the names, some other possibilities were Iturup, Urup, Sakhalin, Kolguyev, Paramushir... :) Kotlin ain't so bad, then