Comment by etripe

5 years ago

> IDEs and code generators have replaced a lot of the more stupid boilerplate. Not that there isn't a lot of stupid boilerplate in Java but it's been greatly reduced by tooling.

I've been dealing with Java for about four years in an academic setting, not professional. When observing Java code in workplaces, the code bases have universally been bloated monstrosities composed mainly of anti-patterns - but that was hopefully down to the era these applications were written in (J2EE, Struts, etc).

My experience is that:

* Auto-generated code is still visual (and debugging) noise

* Annotations like Lombok's are great, but are "magic" abstractions, which I find to be problematic. They add cognitive load, because each specific library has its own assumptions about how you want to use your code, as opposed to built-in language constructs.

* Especially for Lombok, I can't help but think adding @Getter and @Setter to otherwise private fields is a poor workaround for a simple language deficiency: not having C#'s properties { get; set; }. I feel the same about libraries that try to circumvent runtime erasure of generic types.

* Compared to C#'s ASP.NET (core), I find fluent syntax configuration with sane defaults and "manual" DI configuration much more manageable and maintainable than auto-magic DI as in Spring, because at least it's explicit code.

* Java tooling (and if previous points weren't, this is definitely a subjective stance) just seems inferior and to set a low bar in terms of developer experience - maven or gradle compared to nuget, javac compared to dotnet CLI, executable packaging... As to other tooling, I suppose you're mainly referring to the IntelliJ suite?

I think C# (and by extension, Kotlin) had the right idea in seeking a balance through removing as much boilerplate as possible in base language constructs. Adding libraries is fine, but shouldn't be a workaround to evolving the language.

I agree with all of your points, including C# and Kotlin approach.

At the same time, a lot of that are artefacts of decisions made early in Java such as backwards compatibility.

The same for tooling, a lot of tools are results of its time, Ant, Maven and now Gradle.

Lombok has its pros and cons, I use it sometimes but had issues with Lombok and Java upgrades due to how Lombok writes out its bytecode.

I haven't touched javac in more than a decade so I don't really care about it, it's been abstracted away by my build tools (and I agree, the build tools are less-than-optimal).

Again, I agree with all the critics, at the same time just given how large the Java installation base is and by having to go through the Python 2 vs Python 3 migration path, debacles, etc., I still prefer to have all this cruft that is known, well discussed online, has documentation about its quirks rather than a moving target of language features over the last 20-25 years.

Java is too big due to all the evolution it went through, it could have taken different paths and modernised the language? Yes, at the expense of some core design decisions in the beginning. Do I agree with all these decisions? Nope, but who agrees to all design decisions made by their programming language designers?

> Compared to C#'s ASP.NET (core), I find fluent syntax configuration with sane defaults and "manual" DI configuration much more manageable and maintainable than auto-magic DI as in Spring, because at least it's explicit code.

Fluent-syntax, as it exists today, needs to die in a fire. It's horrible. It's abusing a core key computer-science concept (return values) and turning it into something that exists to only save a few keystrokes.

1. You have no way of knowing if the return-value is the same object as the subject or a new instance or something else.

2. It doesn't work with return-type covariance.

3. You can't use it with methods that return void.

4. You can't (easily) save an intermediate result to a separate variable.

5. You can't (easily) conditionally call some methods at runtime.

6. There is no transparency about to what extent a method mutates its subject or not. This is a huge problem with the `ConfigureX`/`UseY`/`AddZ` methods in .NET Core - I always have to whip-out ILSpy so I can see what's really going on inside the method.

Some libraries, like Linq and Roslyn's config use immutable builder objects - but others like ConfigureServices use mutable builders. Sometimes you'll find both types in the same method-call chain (e.g. Serilog and ImageProcessor).

What languages need is to bring back the "With" syntax that JavaScript and VB used to have - and better annotations or flow-analysis so that the compiler/editor/IDE can warn you if you're introducing unwanted mutations or unintentionally discarding an new immutable return value.

  • > exists to only save a few keystrokes.

    It does that, but it also makes your code read more like natural language. Perhaps I was careless in my wording, as I meant to point to manual, explicit configuration rather than fluent syntax per se.

    As to your bullet points: I can see where you're coming from. I still think it's better than the invisible side effects and invisible method calls you get with annotations.

    > What languages need is to bring back the "With" syntax that JavaScript and VB used to have

    As far as I know, With... End With is a weird cross between "using" in C# and object initialisers. How does that help prevent mutations? One of the code examples (0) even explicitly mentions:

       With theCustomer
            .Name = "Coho Vineyard"
            .URL = "http://www.cohovineyard.com/"
            .City = "Redmond"
       End With
    

    I honestly don't see the big difference with either:

       var customer = new Customer {
           Name = "Coho Vineyard",
           URL = "http://www.cohovineyard.com/",
           City = "Redmond"
       };
    

    or:

       var customer = Customer
          .Name("Coho Vineyard")
          .URL("http://www.cohovineyard.com/")
          .City("Redmond")
          .Build();
    

    [0] https://docs.microsoft.com/en-us/dotnet/visual-basic/languag...