What I like about Java is that it is a deliberate late-mover language. It doesn't go for the latest shiny thing but looks at what features have proven themselves in other languages.
I can't help but to be a little sobered by the promises of Kotlin and C#. They look great on the surface, but once you have more complicated code to work on they tend to show warts quite fast.
Java is remarkably consistent and really doesn't have any sharp edges. Even with generics and type erasure, I found myself fighting the language semantics in C# way more often than Java.
> I found myself fighting the language semantics in C# way more often than Java
What's an example? I find C# quite well designed despite the Chinese menu of features.
> Java is remarkably consistent and really doesn't have any sharp edges. Even with generics and type erasure [...]
These back to back with a straight face...What is a sharp edge in your mind?
I personally enjoy Kotlin. There are potential warts with it, but those exist with other languages.
That said, Java taking it's time and observing the playing field is a huge benefit. And now that they've been slowly delivering new language features, it's really made it pretty nice flow of powerful well thought out extensions each release. There hasn't been one of the new features that I think missed the mark.
Java lacks a ton of quality-of-life features that are table stakes in other languages, including C# and Kotlin. Auto-properties, async, extension methods, nullable reference types, primitive types, string interpolation/templates -- Java is inferior to C# in every one of these areas.