On my university no one got Sun's marketing money in 1998, yet the distributed systems, compilers design, and graphics programming, all adopted Java as the language for new teaching materials, as it sorted out several problem with the assignments.
Both awt and then Swing were pretty bad for anything beyond toy programs, though.
Which is what the respective department was doing before.
Swing is great, it does suffer from bad defaults though.
Anyone serious about it, would be getting books like Filthy Rich Client, and additional libraries like JGoodies.
Granted, they only came to be around Java 1.4 timeframe.
Swing was nice to program, but had sluggish reaction time and more than a few bugs (speaking of Swing 0.9/1.0, never went much further).
Also of course the misuse and overuse of classes when designing systems, but the complexity at design time is also the counterpart of the ease of use of well designed APIs. I remember using an IDE to write some code and it skipped the whole documentation and run/compile error phase, I could just cast mismatching types as needed.
Probably C# and Swift/Objective C would only be above Java as top languages, being actually funded by paying customers instead of used by companies that depend on volunteer developed open source software and end-users that equate paying for software with evilness.
Everything beyond 8 has been nice to have but 8 was the big one.
(I'll ignore Kotlin... I don't get why people like it.)
Fast forward to around 2020 and I had to organize a team to build search engine in JVM. Given how hard it is to find decent Scala developer I surrendered and did this project in Kotlin. Java devs easily pick up and write sensibly FP-style even code, and builtin coroutines are cool. Even though I had to open up the internals of coroutines to get tracing to work they felt a very welcome change compared to say monadic explicit async.
So for me Kotlin is kind of watered down Scala for typical Java folks and I could tolerate it compared to perspective of writing plain Java.
Heh, I once had to work in a code base that used an `Integer premiumDefenseLevel`. It contained the level of premium defense a player had purchased, or null if the player didn't have any levels of premium defense purchased in the recent times.
This was in fact annoying to remove, because a 0 after a 1 was treated differently than a null some time for advertisement reasons: Someone who had bought premium defense in the past is more likely to re-buy than someone who didn't, so they are pestered with ads. But it eventually turned into a neat little domain object.
That's a deal-breaker. If you can't tie together a few values in a record/struct without being forced to box them/heap allocate them, the language will never be used anywhere where you even remotely care about performance.
This is not a difficult problem to solve - C# did it in a very elegant way with structs, and as a direct result, probably for half of all videogames written today, the gameplay portion is written in C#.
For a very long time it completely stagnated and all the features we see added now should've been there 10 years ago, at least, and a bunch of other improvements on top. All because "our goal isn't to adopt the strategy of less successful products, but to forge our own" which now turned into busily adopting all the features that the "less successful products" have had for years: https://news.ycombinator.com/item?id=28985688