story
I use Go as a "better C". Though I'm honestly disappointed with even that. My current company, we built an image processing service in Go. It performed poorly and had poor stability (the imagemagick bindings appear to be half-baked). I rewrote it in Java and it is faster, more stable, and the code is much cleaner.
Honestly, the next time I need a "better C", I'll probably pick up Rust or D.
YMMV.
Are you saying that Java is better about any of that?
Just compare Java streams with Go container classes. Go's aren't typesafe (though that will hopefully change when generics are officially released) and almost every operation requires imperative code. And endless `if err != nil return err` every time you want to call a function - which actually destroys useful stack information.
I won't apologize for the crap Java code out there - but you can write crap in any language. Modern Java is capable of producing pretty, svelte code.
Can you elaborate on Java streams vs Go's containers? I assume you mean things like List and Heap in Go? I'm not sure why you'd compare those to Java's stream API rather than Java's collections. In any case, I do agree that Java's standard library has WAY better collections than Go does, and Go doesn't have the excuse of wanting a minimal standard library.
However, I'll push back a bit on the complaint that working with Go's containers/collections/whatever requires imperative code for everything. Now, I'll remind myself that one of your original points was that Go was "actively hostile toward functional programming" and I retorted to imply that Java was just as bad at all of the things you mentioned. I'll concede that Java isn't actually quite as hostile toward functional programming as Go. But, I'll move the goalposts a bit and claim that supporting some few functional programming patterns isn't inherently good and doesn't automatically make a language better.
> And endless `if err != nil return err` every time you want to call a function - which actually destroys useful stack information.
I agree and disagree. I'm one of the few people who still thinks that checked exceptions are a good idea for a language. I have my complaints about how they're implemented in Java, but I think the concept is still a good one and I honestly think that even the Java implementation of checked exceptions is mostly fine. The issue, IMO, is with training and explaining when to use checked vs. unchecked exceptions and how do design good error type hierarchies.
Go's idiomatic error handling is mostly stupid because Go doesn't have sum types. But, I'd argue that if you are wanting stack information, it means that you shouldn't be returning error values at all- you should be panicking. Error values are for expected failures, a.k.a. domain errors. You can and should attach domain-relevant information to error values when possible, but generally, there shouldn't be a need for call-stack information. A bug should be a panic.
Go is typically structured with many relatively small binaries. Each binary can be relatively self-contained.
The way I've seen Java used, it typically has fewer binaries with each binary bundling many services. Many of which include clients for services at the company but a different org - where that other org can just provide a Guice module that sets up the client to call their service and anything that needs it can easily inject it.
I still hate Java but, damn, I see why it's used at B I G companies.
Also, Go has really poor abstracting capability, which may be good for small code bases where having abstractions is a detriment, but abstractions are the only way to handle complexity. If you have the logic spread out over many different parts (or God save us, copied code!), a new programmer will have much more trouble picking up what the hell is supposed to happen.
In the extreme case, compare reading assembly to a high level language. Sure, each instruction is trivial in the former case, but you have no idea what does the whole do.
Java is in the unique position of excellent performance (state of the art GC, very good JIT compiler) and observability with no-overhead real time options. Due to the language having multiple implementations of a standard and it being one of the top 3 biggest ecosystem, it is nothing like Cobol. You can say it is legacy for 3 decades to come, but it will not die. Hell, it improves with a never-before seen speed.