Hacker News new | past | comments | ask | show | jobs | submit login
Lombok Saved My Ass (devolution.tech)
69 points by tuczi on Nov 16, 2019 | hide | past | favorite | 58 comments



While I can understand why somebody would want to use Lombok, this is actually very misguided.

If you want to program in better language, just go and use better language.

The most important strength of Java and basically the only reason it is being used is that the code is simple to understand (simplistic!), everything is easily trace-able and debuggable and that you get fantastic tools that know everything about code and can provide you safe operations on huge code bases (refactoring, finding usages, tracing and understanding code paths, etc.)

Lombok and new Java features decrease reliability of tools removing Java's biggest strength.


I have the complete opposite point of view. I never used @Cleanup but at least the @Data annotation makes it much more readable IMO.

There is tons of boilerplate in a simple Java bean, so when you have 3 pages of getters/setters, equals, hashcode, toString etc... and someone introduces something hacky it doesn't jump out at you.

To be fair, it doesn't have to be a hack, but it's doing something weird that's unexpected.

Then if I use @Data and I still see a Getter/Setter defined it calls it to my attention since odds are i'm doing something different.

ie. Oh, you're parsing a string get a numeric value and then setting it with a fallback value/exception. Okay good to know. If it screws up at least I know there is a special behavior here.

Lombok makes these patterns much easier to read though I prefer to only use it for repeat code. Saving one line so you can have @Cleanup doesn't seem worth it to me. It hides too much as you said above, but it varies on the use case.


I still can't figure it out. Why do people still use auto-generated Getters and Setters instead of just making the field public? What is the advantage of using Beans these days? I've never run into a situation where I wanted my getter/setter to do something other than return/set the value.


Mostly just convention, but it's also a more flexible pattern. A public instance variable cannot have public reads, but private writes for instance.

There is also the case of derived properties. e.g.

LocalDateTime ranAt

LocalDateTime finishedAt

getRuntime() { ... }


> but at least the @Data annotation makes it much more readable IMO.

Record types for Java are being worked on, and we should be getting them hopefully at some point in the (near) future.


> Record types for Java are being worked on, and we should be getting them hopefully at some point in the (near) future.

Just 2 days ago JEP 359 [0] was marked to be fixed [1] in Java 14 (as preview feature)!

[0] https://openjdk.java.net/jeps/359

[1] https://bugs.openjdk.java.net/browse/JDK-8222777 (see "History")


If you use @Data you say that all fields will be available with getters and setters that will have no other special logic.

Why not consider making those fields public then and not bother with @Data?.

This is how Java language was designed, then everybody started mindlessly putting getters and setters around private fields to pretend they are private while giving everybody access to do whatever they want with them (silly).

Now you are putting @Data to further muddle the problem which basically does not exist.


hash, equals, toString.


Records are strange feature. They are immutable, they can't be abstract, they can't extend, they have implementations of some methods. I don't need anything of that. I just need short syntax to declare property with trivial getter/setter and that's about it. This is strange proposal.


The concept of records already exists in other languages, including JVM languages (like Scala and Kotlin). The fact that they're immutable and final is important. If they weren't, there would be subtle bugs caused by that. Scala actively recommends that case classes not be extended precisely due to this fact. Overriding methods for equality and hash code make it such that these classes can be used as keys in maps or entries in sets.


Records are strange feature. > They are immutable, they can't be abstract, they can't extend, they have implementations of some methods. I don't need anything of that.

Some people find data transfer objects to be very useful, even fundamental to implement a well architects software project.


If you want an object with a few getters and setters, just make a class.

This proposal is to remove the boiler plate of creating immutable objects with value semantics.


never soon enough!


Absolutely agree that Lombok increases Java readability. If I cannot use JVM alternatives like Kotlin or Scala (for whatever reasons), Lombok is a perfect tool to help beeing productive.

IMO saving one line with @Cleanup is not worth it but if the class doesn't implement AutoClosable, I think it makes code more readable too.


If you absolutely, really need @Data, why not consider making your fields public? I mean, if the only access semantic is going to be exactly the same as public field why have getters and setters in the first place? It just seems dumb to me and not how Java language was designed in the first place. For me it is trying to fix problems of one bad pattern with another bad pattern (now you have two problems).

Readability depends on how large work you do and how much experience you have. Readability is not just how much code you have, it is about using consistent patterns to solve your problems. Readability is how fast and how safely you can move around the code to understand how it works at any level of complexity.

I have 17 years of experience working with Java commercially.

I am blind to getters, setters, builders, and bunch of other stuff. It does not bother me, I can recognize patterns immediately and just ignore them.

What bothers me is when I don't trust my IDE to find all uses of something because some jackass decided to add Lombok to the project and now my IDE does see all code exactly as it will be running.

This is much more of a readability fail.

When you make global refactorings on an application that has over a million lines of code it is absolutely crucial you can trust your IDE to find all uses of something.


This is true as the point of getters and setters is encapsulation, if you totally ignore it there is no real reason to have them at all and public access is a language feature to use.


I somewhat agree with you and reluctant to use Lombok. But it's easy to say "use better language". Kotlin is too different. Java is actually pretty good right now and the only thing I really need is property declaration.

Actually I was thinking about different approach last year, didn't get to implement it. Something like declare abstract class with getX() setX() methods and let library to implement missing methods. Now it's one line per method, no trivial code and able to generate other nice things, e.g. I really want to have fast initializeable properties (like getX throws exceptions until I call setX).


> (like getX throws exceptions until I call setX).

Oh, the wonders of using widespread mutable state! Did you remember to call this other method here before trying to call me??? No?!?! Oh, here's a nasty Exception for you then.


It has nothing with mutable state, actually. I can use this approach with immutable state as well. It's just another value "uninitialized" for property. Like `null` but throws faster (and potentially separate from null, because null could be a valid value).

I'm using it for SQL queries. My query returns subset of some column set. It's not practical to define a separate class for every query. So all queries for the given table returns the same class representing all columns in a given table. But if one method returns subset of those columns, other columns contain uninitialized value and should not be read. Any attempt to use uninitialized value is a bug and must be caught as soon as possible.


"It's not practical to define a separate class for every query."

I'm not sure it's really impractical, at least in my experience. You may not need a separate type for every query, as long as you are willing to have some queries select a few more columns than you actually need right now; and you don't need separate classes, just separate interfaces, which makes the amount of extra code you need smaller.


For non-toy projects, those strengths greatly outweigh the language's flaws.


It's not as simple as use a better language. Often, various organizational constraints Are preventing this. I tried building a new micro-service in Kotlin, right after spring released official support, and there was an incredible reluctance for some team members to learn it. The service was still spring boot with most of the standard patterns but just different enough to make some uncomfortable. Inertia at big companies makes change hard

Edit: The kotlin service was one internal service that I experimented with. Our other 17 production services were standard java spring.


How does Lombok decrease reliability of tools?


Lombok creates new code that is not present in the source code. This throws off tools in some cases.


What do you mean by 'not present in the source code'? Annotation processors generate code before the whole codebase is compiled, any tool can analyse the generated lombok source code, the same as with any other code-generating annotation processor.


You do have a point, but if a tool is able to parse Java them I believe it is trivial to extend it to support Lombok, which by now is a quasi standard and fundamental component of a lot of java projects.

Edit: why anyone in their right mind would downvote this comment?


Probably because there is enough people that also value being able to always trace the code written by less experienced team members?

Hint: it is in bad manner to discuss voting on HN. Voting is a popularity contest. If you want your comments to be popular try to be pleasing to everybody and if you don't, don't get upset by downvotes.


> Probably because there is enough people that also value being able to always trace the code written by less experienced team members?

You can trace/debug lombok generated code though...


It’s not that hard to just run delombok and then run these tools.


While I tried to use Lombok in Java 8, it never felt safe. Even if I enabled the integration with the IDE, it magically patches my code under the hood so it wasn't easy for me to catch the bugs introduced by Lombok.

> There is little gotcha when using Java’s try-with-resource syntax. The resource has to implement AutoCloseable interface, so the compiler can use void close() method for closing a resource. Lombok’s @Cleanup, it doesn’t have to implement this interface. The resource just needs to have some closing method which by default is void close() (but you can specify your own method name responsible for closing resource).

Specifically, I don't agree with this approach in a compiled language like Java. We need to have a standard writing code in Java and AutoCloseable is the way to go. If the library doesn't support it, just create a proxy class and implement the interface there. It may be a bit verbose (we switched to Kotlin later on) but it will be much easier to catch the bugs. I don't want to spend 4 hours trying to find the root cause of a strange bug in favor of writing less code.


I used Lombok back in the day (never by choice). It's somewhat useful but I never really liked code generation and always felt it's a bit of a kludge and it indeed confused the hell out of intellij regularly (which is not that hard, it gets confused all the time). It's syntactic sugar. A bit of a stop gap to make Java look a bit more like several other decent languages out there.

Kotlin is indeed far superior and an extremely easy drop in solution on pretty much any Java project. A lot of what Lombok tries to achieve via code generation triggered by annotations is just built-in to Kotlin. E.g. a large part of what Lombok does is making java beans less painful by generating setters/getters and lots of other stuff. That's useful. However, Kotlin's data classes are way better.

For things that are Closable/Autoclosable, Kotlin injects a use extension function that takes a block where you use the resource and it cleans up after you. You just do a stream.use {...} and it does the right things. Kotlin adds tons of useful stuff like this to existing Java APIs.

Whether you use Java or Kotlin, any half decent static code analysis tool should be able to help you spot shoddy resource handling. Same for Kotlin; it has a lot of this built into the compiler. For Java, use spotbugs or similar. IMHO that's not optional.


Agreed - lombok is a kludge for language features, but back when it was big there weren't a lot of alternatives beyond switching to an entirely new language and stack (a tough thing to sell in large enterprise).

Regarding IDE support - I was once on a project using Lombok where some developers used Eclipse and others used IntelliJ. The Eclipse devs wanted to use this new @Builder annotation, which didn't work on IntelliJ but worked fine on Eclipse - for interesting reasons.

I attempted to patch the IntelliJ Lombok plugin to support @Builder - but gave up on it for reasons of time, priority and the fact that I wasn't certain it would work as Lombok continued to change. Also this was an experimental feature at the time that wasn't even certain to become mainline. I learnt a bit in the process.

Lombok works by passing certain switches as arguments to javac, which alters the class files that it writes out based on the annotations. It "confused the hell out of IntelliJ", because IntelliJ's intellisense/auto-complete plugin ecosystem works as so: IntelliJ exposes AST _from source_ to plugins, like the Lombok plugin, which hook in and let IntelliJ know, for instance, there's another method here due to this annotation. So it's constant effort to ensure the plugin's injections line up with what the compiler is actually spitting out.

Eclipse on the other hand is always aligned. I can only infer that it generates its autocompletion etc from the _compiler's output_. So whatever version of Lombok you're using, the autocompletion features will always be aligned.


I've used a few pojo-gen libraries, but they've never had good integrations or workflows with IDEs, and they usually led to a flakier build. Editing the definition file was never as good as editing a real class.


FWIW Lombok enhances byte code, I don’t think it does code generation. Code generation is awesome specially when it works ;)


I don't know whether Lombok routinely generates source or bytecode, but its “delombok” command can definitely generate the equivalent Java boilerplate for whatever you use.


Relative to toponym: a few years ago I got bitten by an unidentified creature on the beach in Lombok and nearly died of asphyxiation due to a severe histamine reaction and the length of time it took to get to a hospital. Really severe .. said my goodbyes to my wife. Never had anything similar in life before or since. Luckily we did reach hospital with a couple of minutes to spare and the local hospital sorted it pronto (oxygen and loads of antihistimines). The cost was minimal. Life really has its ups and downs... too many to care about Java libraries :)


So I've my self used Lombok but I've been using Kotlin lately. IMHO Kotlin if far far superior in terms of how it provides appropriate constructs that are patches to a stale language (hopefully new release cycle fixes it).

So far I've observed with libraries like Lombok there is a smaller community (compared to language community) that vets the idea; and the features are opinionated to that particular community. Which results into fragmentation; just as example https://immutables.github.io/ and https://github.com/google/auto are two libraries providing features like factories and immutable value classes. Then you get articles like https://codeburst.io/lombok-autovalue-and-immutables-or-how-... for improving your code.

This in my opinion doesn't hurt as long as you are a small company. But when your code is going to be used across teams with different opinions, and language doesn't enforce a single way to define construct these kind of byte-code mangling ideas fall apart. I've personally people complain about Lombok and they hate it every bit. Doing these constructs at language level removes debate, and unifies how people in larger community. I hope newer versions of Java move fast enough to make something simple like `@Cleanup` a thing of past. I will highly encourage everyone to try Kotlin too; it's almost transparent drop-in with better syntax.


The irony of people telling me that they don't appreciate Spring or Hibernate for the magic behind the annotations that they don't understand, to then turn around and drag in Lombok. Also, if you are telling me you don't like to write getters and setters, does that mean you are not using an IDE? _Sure_, it's a little cumbersome to write them, but not a lot?


It's easy to write them. It's hard to read them. Say I have a class with 20 properties, it means 40 methods. Now one of those methods not trivial and rest are trivial. How can I quickly find out? I can't. Getter is 4 lines, Setter is 5 lines, 20 pairs is 180 lines of nothing.


Lombok certainly saved my wrists and hands from repetitive stress injury over the years. Manually typing “effective java chapter 1” immutable object implementations can be truly excruciating.


So you introduce a new library that does "magic" instead of using one more line? To me this is the opposite of good code. Let alone that the given example would probably live in some library code anyway.


Lombok is a compile time library that doesn’t do any magic but replacing an annotation with fragments of code. It feels more like using macros which exist in many languages. And it helps in keeping the code readable and DRY.


So the code is inflated because the machine does the code repetition for you. The structure of your program is not getting better I think. I do not argue against annotations but in this case, if there is such a simple thing as try with resources, just use it. If your code is sprinkled over with annotations like this it is hardly getting more understandable. From my experience, a single annotation comes often in packs.


Lombok's @Data (which is probably 90% of why people use it) is essentially equivalent to Ruby's attr_accessor. It's not "magic" so much as small and useful modification to the language.


There is a problem with JaCoCo, a Java code coverage utility, and try-with-resources. It looks like the try-with-resources creates byte code branches that are unreachable. In the JaCoCo report the try-with-resources line shows up with 4 or 8 branches (I forget which) and most of them are not reachable. That is to say, no matter what kind of unit tests you create (essentially there are two, a test where the code throws and a test where the code does not throw) you can not hit all the possible branches that JaCoCo detects.

This caused a problem for me because we would write very lean code with low cyclomatic complexity and having these missed branches would screw up our code coverage numbers, causing us to miss our standards. I would just do it the old way, with the finally. I wish I knew about this @CleanUp. For some reason a lot of code i am writing nowadays does not have me messing with streams directly, but when it comes up again I will use this annotation.


> This caused a problem for me because we would write very lean code with low cyclomatic complexity and having these missed branches would screw up our code coverage numbers, causing us to miss our standards.

This seems like a perverse criticism. Fix your code coverage analysis?


    OutputStream out = new BarOutputStream();
    in.transferTo(out);
    if (out != null) ...
Can someone explain how `out` could possibly be null here?


How it's written here it won't be null. However the general pattern is to check for null in the finally block since it may not be provable that the stream is non-null (e.g. created in a library call or similar). In fact the try-with-resources feature handles the null case as well.


In the article, the if check occurs in a finally clause. So the code would execute even if the constructor threw an exception. Hence the need to null-check it first.


But that just isn't true. Neither finally-block appends a try-block that initializes the stream that it null-checks.

If the `in` stream init throws, the entire try-block isn't run at all. If `out` stream init throws, the following try/finally block isn't run either.

Lombok's docs get this wrong too. Unless I'm going crazy.


I've had better luck with patterns that take lambdas (and internally do the try-close) because they're impossible for users to screw up.


I was really hoping this was about the Indonesian island


Lombok is named after the Indonesian island, because Java is as well. (https://en.wikipedia.org/wiki/Java)


Same - I was hoping for an epic surfing story at desert point


I used Lombok many years ago, but stopped. Modern Lompoc, with tools like delomboc to generate plain Java source code, and better tooling support beyond Eclipse makes Lompoc look pretty good now (if I still used Java). Cool project.


What is "lompoc" coz google does not help me find it??


It's a typo for lombok, I believe.


It’s a city in Southern California, obviously.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: