I think you're (perhaps intentionally) overestimating the hate the JVM gets to paint Java critics as unreasonable, but the fact of the matter is that the JVM and Java the language are light years apart in quality and (subsequently) the attitude they get from their users. One is a decent VM with man-centuries of work and inspiration from academia (Self, whose VM heavily influenced hotspot), the other is an ugly mess of a language whose syntax and semantics is pure unfiltered paperwork and bureaucracy.
I have elaborated plenty of times on why Java is a 1980s language that were obsolete as soon as it was released, the latest is my comment on https://news.ycombinator.com/item?id=32128271 https://news.ycombinator.com/item?id=32128271, which I reproduce in full at the very bottom of this comment to save you a ctrl-f.
>loom, valhalla, graalVM
Every single one of those has nothing to do with Java and everything to do with the JVM as a high-tech psedo-OS that challenges the classic preconceptions about performance-productivity tradeoffs. I don't want to attribute dishonesty to you, but again, I see absolutely no reason to confuse a VM with a (incredibly inferior and badly designed) human-level programming language just because it happens to be the first language to run on the VM.
>frankly believe the only other commercially viable language that has similar philosophy to JVM development, is Rust.
Come again? How is Rust similar or even comparable to the JVM ?
----------
Reproduced Comment
----------
>>>>I'm a Java hater. Here are the reason I hate it for
- Baking the difference between primitives and objects into the language itself : an ugly mistake with far reaching consequences, made by a language designed in 1995 while another designed in 1980 (smalltalk), in 1991 (Python) and 1995 (Ruby) all didn't fall for it.
The difference is an irrelevant VM-level optimization detail, there is no reason to uglify the human-level language with it. Once the initial mistake has been made, the correct response was NOT to make the even uglier hack of wrapper classes, but to make the primitives objects in the newer releases of the language, this won't break old code, as valid uses of objects are a superset of valid uses of primitives, except perhaps that objects need to be allocated explictely with "new", but this can be a special case for primtives (i.e. "int is a special kind of object that you don't need to allocate explicitly"). The compiler can figure out whether it needs to be represented as objects or as primitives, you can leave hooks and knobs for people to tell the compiler they need to the primitives to be represented as primitves, but it shouldn't be mandatory.
- Baking in choices about object representations : Like the fact that objects are always passed by reference, or that they are always allocated on the heap. Why the "always" part ? why not give developers the choice between pass-by-value and pass-by-reference like C# does ? why not give developers the choice to allocate on the stack (and complain as loud as you want when they want to do something unsafe with it, like escaping from methods), which, unfortunately, even C# doesn't ?
Everytime you see something like "foo deepCopy()" that's a failure of the language, forcing you to explicitely pay attention to the fact that foo objects need to be copied deeply everytime they are copied, instead of just once when you define the object by marking it as a "struct" or whatever word to signify that object has value semantics, and then deep copy is just assignment or passing as a parameter. Why make it the default to be inefficient with the heap when it's very easy to give developers the choice to be efficient in situations where it's always safe ?
- No operator overloading : I get the hate, it's a powerful tool. But it's misguided to ban it, operators should not be special, languages like Haskell and Raku go even further and allow you to define new operators entirely and control their predence and other things. You don't need to go that far, why can't objects use the already built-in symbols the language support ? because it might be confusing ? anything can be confusing, you can write assembly in any programming language, and it will be even worse than assembly because of the more powerful and obscure abstractions.
- Generics : The overall theme of forcing you to do things its way seems to a staple with java. Why do I need to use type-erased generics ? why shouldn't I get the choice to specify whether I need a new class generated for runtime efficiency or use the type-erased catch-all for size efficiency? there is no need to bake VM-level support for this, it can all be done at compile time (possibly with help of additional metadata files or special fields in the .class of the generic type).
- Overall verbosity : Why "extends" and "implements" ? do you really need to know whether you're inheriting a class or an interface ? and can't those be lighter symbols like "<" and ":" perhaps ? why is "private/public/protected" a must in front of every method and field ? most people align fields and methods by their visibility, C++'s way is that you declare "public:" and then everything declared below that is public. In the worst case you can always recover Java's way by "public : <method> ; private : <method> ; public : <method>" and so on, but it's nice to at least have the choice of not repeating yourself.
Why aren't any constructors generated ? there are at least 2 very obvious ones : the empty one, and the one that assigns all the non-defaulted fields (and can take optional arguments to override the default fields). Why aren't generated getters and setters available with a small and light request, like C#'s "get ; set ;" ? Java's design is just full of things like this. It feels like a weird sort of disrespect for your time, "yeah you must write those routine 25 lines of code all by yourself, you have anything better to do?", how about actually writing my application instead of pleasing your language with weird and unnecessary incantations ? It's like a modern COBOL.
- Horrible OOP excesses : Not really the language's fault (except that it encourages verbosity and loves it) and already mentioned, but worth mentioning again.
Overall, I treat java as assembly. I write kotlin in my spare time, and whenever I'm confused about the semantics of some construct I make intellij show the bytecode then hit "decompile" to see a Java rendition of the code, the exact semantics will be obvious but verbose. A language that took this literally is Xtend, a high-level augmented java which transpiles to java and is a strict superset of it, but with option that the Xtend compiler figures out all the verbosity for you. Groovy also takes the "Superset and Augment" approach but doesn't transpile. And off course Kotlin is very good with it's interoperability, every JVM language is but Kotlin's mixture of being close to Java semantics (unlike say, Scala or Clojure) and Intellij excellent support for mixed projects makes it at least somewhat special.
I like the JVM and it's cutting edge research and performance, and these days the Java standard writers seem to show signs of finally waking up to reality after years of being behind every mainstream language, and they regularly augment and modernize the language. But you can't undo 20 years or so of bad design, not easily and not painlessly.
>Indeed, tooling is the new syntax.
Very much agreed, long long gone are the days when a compiler or an interpeter is the only thing expected out of a language. But it's not a panacea to treat any bad design, at best it's just a band-aid for bad designs that makes them barely berable. The language has to be designed from the start with the knowledge of "this is going to run in an IDE" baked in to make full use of the full range of fantastic things an IDE can do.
---------------------
I won’t reply to all of these, but primitives:
With the initial, interpreter-only operation of the JVM, performance considerations were very imminent. Generics, given the context made a correct choice, and the maintainer team’s vision even back then was quite right, Valhalla seems to be able to heal the rift between primitives and objects. So in this view auto-boxing was again a sane choice.
Regarding stack/heap allocation: I believe the beauty and longevity of Java code is partially thanks to their avoidance of over-specifying language semantics. Sure, C# may have won a few percent better performance by introducing yet another feature, pushing the responsibility onto the developer. So only a select few programs are effected. While Java’s continued improvements to the JIT compiler, GC and escape analysis brought even decades old code bases written for the first versions of Java considerable performance upgrades for free. Of course, value types are still an important performance win left on the table, but it will be solved - yet again, in a not over-specified way. Primitive and value types as per the current view only specify semantics, leaving the allocation strategy up for the JIT compiler to optimize.
Operator overloading is a difficult feature, we have seen plenty of failed attempts and a few where it seems to work fine. I also think that partial operator overloading (a la Rust, adding an Add, Multiply, etc interfaces with single methods corresponding to + and *) could be useful, but left uncontrolled it can be a catastrophe ( a <<! b, what does it mean?). Nonetheless, it is not that big of a pain point, using BigInteger::add 3 times from time to time is more than fine.
Generics were executed very elegantly in my opinion given the constraints. It has some edge cases, but reification is overblown as a problem and overloading may come once primitives and objects are unified.
Verbosity: come on, seriously? I didn’t benchmark it, but I am fairly sure I actually press less keys in a good IDE writing Java than writing the equivalent in a “more concise” programming language , while the end result will be that much more readable. Like, honestly you believe that programming takes long time because you have to write class A extends B instead of class A <: B? Like, you spend multiple orders of magnitude more time over a single line thinking. Writing is never the bottleneck.
Have you read the JEPs? The primitive/object split is being fixed and value classes are coming. The rest of your post really just seems like preferences, personally I do not like operator overloading (scala ptsd), and I don't see the value in < or : over extends and implements, we all type > 120 word per minute, and I think Goetz's plan for withers over records will solve the properties problem.
The fact that an experienced developer has to transcompile Kotlin to Java to understand some parts of it also doesn't seem like a plus to me.