Note that at least one of the features described, (static) Null-Safety, can be achieved in Java via annotation processors. For example, by a tool such as NullAway (https://github.com/uber/NullAway). Yes, adding '@Nullable' is more verbose than '?' (thus is the Java way :) ). But there are plenty of projects were it still makes sense to use Java over Kotlin. This is particularly true for existing codebases, where adding annotations is certainly easier than rewriting everything in a different language.
Disclaimer: I am a contributor to NullAway. There are plenty of other nullability static analysis tools for Java worth considering (Checker Framework, Eradicate, etc). NullAway's main strengths are a focus on performance and the idea that you should be able to use it as part of every (local) build of your code.
There's a huge advantage to having language level support for null safety. Annotations are just metadata, and managing semantics of that metadata is pushed to the programmer: to configure a static analyzer to act on them, to figure out the differences between different backends, make sure it's enabled in the build, write/install IDE plugins, etc. Language level support for this stuff means programmers don't have to worry about any of that.
Pragmatics of course necessitate that we sometimes need to tack these static analyzers on after the fact. But over time, the amount of tack ons and bandaids and unnecessary backwards-compatibility-necessitated complexity becomes so high that you're better off using a better language, if you and your team can handle it. For many people, this has already happened with Java. I kind of wish Java the language would just die already and stop adding just enough features to keep people from moving on to the next generation of languages. Science progresses one death at a time.
> Annotations are just metadata, and managing semantics of that metadata is pushed to the programmer: to configure a static analyzer to act on them, to figure out the differences between different backends, make sure it's enabled in the build, write/install IDE plugins, etc.
Sure, but keep in mind switching to a new language also entails a huge investment in tooling. There is definitely a higher cost in switching your build infrastructure, tools and IDE plugins to support a new language (nevermind porting an existing codebase) than the few configuration lines it takes to add an annotation processor. At a certain scale, the tools you need might not have even been built for Kotlin, while they have existed for years for Java.
> For many people, this has already happened with Java.
Yes. And, for many others, it hasn't.
Look, I am not arguing that we should all be writing Java until the heat-death of the universe. Whether it is Kotlin or not, I am sure better ways to do things will pop up. But there are good reasons to start even many kinds of new projects in Java today: maturity/efficiency of the compiler, tooling support (including static analyzers, since nullability type systems are not the only feature you'd ever want to bolt onto a language), etc. Over time those reasons will be less, but even then, existing Java codebases will remain, and it will still be worth making them safer. Is not like C, C++ or even Fortran codebases are not around anymore.
Kotlin has the best java interop for any of the JVM languages, so you don't have to rewrite everything in kotlin . You can gradually migrate while using the same tooling.
And related to the parent comment: Kotlin's compiler understands several kinds of @Nullable/@NotNull/@Nonnull annotations, and treats them as Kotlin's nullable/not nullable ("@Nullable String" is treated as String?, for instance). In the other direction, Kotlin's nullable/not nullable is visible to the Java side as one of these sets of annotations (IntelliJ's IIRC), which allows for instance annotation processors like Dagger 2 to know when something can be null or not.
Interested in how you think Kotlin interop is better than Groovy? Curious because that to me is one of the main things that makes me choose Groovy in some situations over other languages (eg: Scala, Clojure, Jython, etc) where the impedance mismatch is much higher.
Disclaimer: I am a contributor to NullAway. There are plenty of other nullability static analysis tools for Java worth considering (Checker Framework, Eradicate, etc). NullAway's main strengths are a focus on performance and the idea that you should be able to use it as part of every (local) build of your code.