>'public:' / 'private:' / whatever may save you a couple milliseconds when defining a new class

The issue is not at all the time you save at write-time, this is completely unimportant and automated by [IDEs|text editor macros] anyway, the issue is reading the code later. Imagine that every time I write a sentence I prepend "Banana699 says" or "Banana699 thinks", this could be automated enough to be non-bothering to me as I write it, but imagine the sheer annoying line noise that will be my paragraphs with this. This is also bad for me (the writer) because I read and re-read my words when I write, having it full of noise clutters my thought process and impedes thinking about important things.

> I personally often don't group things by public/private, but by their utility (e.g. blocks of fields by what part of state they store)

You can still do this in C++, the class declaration can contain any sequence of 'modifier: declarations" clauses. (At the very extreme end, you can simply prepend 'modifier:' to every field and method and just have your java syntax back with an additional ':', the fragment "public: int x; private: int y; public: int z;" is valid C++ inside a class or struct declaration and does exactly what you'd think it would. But this is obviousely non-optimal in this case and so many others.)

>you can pretty much ignore variable types

Just like you can ignore my irrelevant username if I put it before every sentence I write, but the mere presence of irrelevant info steals your attention and clutters the code for no good reason.

>look only at variable types

Which you can still get and very easily at that in an inferred-types language if you're using any moderately modern IDE/TextEditor. Haskell is one language whose type inference can feel like magic sometimes, and yet a simple hovering over the identifier in even a lightweight IDE like repl.it reveals what you want. It's not a problem at all. Not all info has to be stored in the raw text, after all even Java is not bad enough to require you to use "new foo.bar.baz.class" even though it would be "clearer" in the strict sense of the word. Type inference simply applies a step further, and you can completely ignore it if you want.

>Having to wrap your static utilities in a class does seem a bit pointless

Meh, it's not a hill I'd want to die on, just a single example that I could have ignored if it was isolated.

>generate a new type for each compiled jar file, and including jar files would then have the same type defined multiple times

This is a bit confusing, I will rephrase it so I can be sure what I'm replying to, you can correct me if I misunderstood you.

You're highlighting the problem that if library B and library C both had a common ancestor library A, and they both use the generic type Ta<int>, then the compiler (or possibly two different compilers) generates Ta_int_Autogen two times for each of them because they are totally isolated. But if a library D includes both of them, then that would mean including two versions and that would cause bad performance and possibly other funny things.

That's C++ template problem, it can be completely avoided in Java's object model because there is (currently) a finite (and very small) number of types that you need to autogenerate. The compiler can simply treat "class generic<A> {...}" as syntax sugar for the following declarations "class generic_for_object {...} class generic_for_int {...} [... and so on for the rest of primitve types]". This is done at declaration time, i.e. in library A itself. All downstream code would then use this one version and this one version only, there is nothing else.

The reason C++ can't do this is that the semantics of C++ generics is literally and explicitely "Yes, this is just copy-paste for every type you instantiate the generic on". The compiler has no other choice but to instantiate on a use-by-use basis. If it started eagerly auto-generating at declaration time, it would never run out of types to auto-generate for. (it can't even know all the types the generic could be instantiated on, neither a common ancestor for all of them.)

>As far as I understand, being able to trivially fall back to non-generic types was also a reason for the weirdness

I don't actually understand what that means. What's the fallback and who's doing it? when and why ?

>But it still manages to be good enough for people

Anything can be good enough if you're constrained enough, what I found surprising is the "liking" part. For me, liking a language means that, in absence of all other constraints and externalities (back-compat, hype and who-else-is-using-it syndrome, perf considerations,...), you would prefer that language over any other. I find it alien that somebody can think that of Java, the language is completely out-of-sync with every frequency in my brain, it's like trying to sing while a rabid dog is barking in your ear.