That the Java language is slow to evolve while the Java platform evolves quickly has been one of the central strategies of Java since inception. In fact, James Gosling wrote back in 1997 how Java should only adopt language features loooong after they've been tried in other languages. It's worked very, very well. There's obviously a portion of users (<10%) who prefer a faster-moving, feature-rich language, and they have good options on the Java platform, but the Java language philosophy has been more attractive to a much larger group of developers.
What is more interesting is not which features Java has chosen to adopt, but which ones it hasn't and won't: yes to lightweight threads but no to async/await; yes to pattern-matching but no to extension methods.
I'm looking at Scala and C++
Extension methods
Async/await -- Java is going with lightweight threads.
* I don't think Java still has string interpolation.
* List, Map literals are not a thing Java has either.
* Value types aren't in yet.
* If as expressions. You have to use ternary.
These aren't complaints. I know what I'm getting with Java. No hate.
Switch expressions can do what if expressions do.
Strings and automatic boxing come to mind.
A) Throwing the kitchen sink in and making mistakes.
B) Changing anything that already works.
There are very few languages that continue to improve and provide such long term stability. More often than not more feature full languages end up forcing me to re-test or rewrite sections of my old code base for forwards compatibility.
Apache, for example, supports a ton of Java libraries for which there's little competition, certainly not well distributed across language ecosystems. PDFBox, Lucene, etc.
When you're in the nitty gritty of automating business processes, that sort of thing is invaluable. For CRUD-style web services, any language will do the trick.
(I say this as not a big fan of Java the language)
Python also has a similarly wide ecosystem, and does better specifically in some regions than any other language, like ML, but a different set of focus areas. It's probably the other top contender for business-process automation, but big enterprises prefer OOP/static typing for typical work.
C# probably comes close in many areas, but there's still a split between the .net core and not-core ecosystems for the long tail of libraries, and no open-source powerhouse quite like Apache in the mix.
(C/C++ might fall here somewhere too, but I don't know nearly enough about those language ecosystems to comment)
We have a Lucene binding, too. It's called Elasticsearch. :)
If you'll forgive a light trolling: how about ahead-of-time compilation, allocating small objects on the stack, UTF-8, compile-time asserts, or SIMD support?
(Was going to include await/async, but then I remembered EA Async.)
I realise this is a little off topic, but the JVM isn't without limitations. (Or perhaps all my examples are wrong - disproof welcome!)
SIMD: https://openjdk.java.net/jeps/338 (to be merged soon), plus, of course, auto vectorisation, which is already in.
Allocating small objects on the stack: this already happens automatically through escape analysis, but more generally (and more importantly) arrays-of-structs: http://cr.openjdk.java.net/~briangoetz/valhalla/sov/01-backg...
UTF-8: What do you mean? Java has supported UTF-8 for a loooong long time. Even the source files support Unicode with UTF-8 encoding.
compile-time asserts: Not sure what you mean. But there's a rich compile-time plugin mechanism: https://www.baeldung.com/java-annotation-processing-builder and even pluggable type systems: https://checkerframework.org/manual/
Async/await: how about lightweight threads, instead? https://wiki.openjdk.java.net/display/loom/
For Async/await, I believe a much better approach is available with Quasar today or with Project Loom soon.
SIMD is an issue though if you need that.
See Project Panama for the encompassing project: https://openjdk.java.net/projects/panama/
You can specify UTF-8 as the default charset (unless you mean for identifiers?) and Grall does exist for AOT.
(For example they have major limitations in Electron, which is one place where having threads would be very convenient. The current best practice is to spin up an entire new hidden browserwindow instead which is frankly insane.)
The goal of the JVM is "write once, run anywhere". v8 achieves the same, but for languages that compile to Javascript. E.g. Dart, Elm, Typescript, CoffeeScript, ClojureScript etc.
I think the real choice to be made is ecosystem. There are languages to suit every programming style in either camp. The availability and quality of packages in the Java ecosystem is higher IMO (use Spring and it just works).
Node has non-blocking I/O by default; it's possible to implement the same in Java, but you have to make a conscious decision to do so. Java has threads, but Node has workers. Java can have a longer startup time and is better suited for servers with high uptime, Node starts up quick and is easier to use in on demand situations etc.
I don't really know why you would, but there is RingoJS if you want to run Javascript on the JVM for some reason.
[1]: https://blogs.oracle.com/javamagazine/java-flight-recorder-a...