I wouldn't be so sure if I was you. A lot of work is being put into JSpecify [1] lead by Google and with most major players in the Java ecosystem participating. If it is a success we might see it in the language one day and not just with annotations.
1) Not crashing-and-burning on simple "Hello World" level use-cases (e.g. working in a Python terminal, a 3-liner script, etc.)
2) Giving actual performance gains in typical cases
3) Allowing code to change in both directions, quickly and easily
4) Integrate well into other parts of the language (e.g. list comprehensions or iterators / generators in Python, or multiprocessing)
I also don't like the amount of verbosity added. I can describe a system which would do all-of-the-above, but I don't think I could have independently invented it. It's based on using JS and Python async / await for a while, and understanding where things were done wrong.
On a mile-high level, I'm also ultimately not sure this is the right model. We:
- Created cooperative multitasking (e.g. in Windows 3.1 and earlies)
- Found pre-emptive multitasking worked better, in part since people were bad at deciding when to release control
- Re-invented cooperative multitasking within apps with async / await
I think there is a more dynamic, higher-level programming model where I describe what needs to be done. Just-in-time profiling and optimization, decides what to do synchronously versus asynchronously versus in a new process versus e.g. on a GPU. An operation like:
[functional_function(x) for x in l]
Ought to sit on CPU synchronously if l is small and f is fast, in their own process asynchronously if they are medium, and on a GPU if they are massive and slow.When you're building software with a design lifespan of decades or more, this stability is advantagous. You don't want to have to go back and make big code changes (ie: pay increased maintanance costs) every few years because the libraries and frameworks you used are moving targets.
Recent Java has just made threads cheap, so async concurrency is much simpler and more consistent with existing libraries and APIs.
It’s not hugely helpful for a UI-bound application which occasionally needs to move some work into a background thread to cut down on UI jank.
Kotlin’s suspending function syntax effectively lets you know which operations are going to (potentially) take a while, so you can make sure the UI thread isn’t blocked, and also make sure you’re communicating to the user that something is happening.
I think that’s a very flawed assumption. Some IO operations are pretty fast, like reading a local filesystem, and can be done in the UI thread, while some heavy non-async functions are accidentally O(n^2), and freeze your IO thread.
And if you say that “of course you should know what you’re doing”, then I think that suspend syntax just adds extra complexity without much semantic benefit.
Probably you can think of a more complex example where it’s not that straightforward, please link code that you have in mind, we can see how with cheap threads it could be made better.
Why make life more difficult when you can have code that reads linearly?
You can always call something async synchronously (by waiting for finishing). Or something synchronous in an async way (by putting it onto another thread). But there is no way to completely remove this fact from the eyes of the programmer. async/await is one of the ways to make it easier to read or write.
In order to replace Java and it's ecosystem one would need to offer MUCH MORE than just null safety...
But certainly if I were to add new features to an existing Java codebase I’d use Kotlin. The interop between the two is fantastic and for me the null safety alone makes it worthwhile.
C# was a better language than Java for some years until Java caught up again.