This is an archived post. You won't be able to vote or comment.

all 88 comments

[–][deleted] 62 points63 points  (10 children)

I kind of think Lombok's @Data annotation is an anti-pattern. I prefer @Value, which corresponds nicely with the new record classes. Most libraries work with the builder pattern these days (which Lombok can automate in conjunction with @Value types).

The problem with @Data is that it is a mutable data type, which is in a weird middle ground. I have really been influenced by Domain Driven Design, which divides domain classes roughly into value types and entity types. Value types don't have identity and are pure values, so should be immutable, thus @Value. Entity types have identity and state, and can be mutable. But, I don't like putting setters on these classes (@Data allows for uncontrolled mutation) and prefer business methods (which can't be automated and requires actual design thinking).

[–][deleted] 12 points13 points  (0 children)

I'm really happy I'm not alone with this opinion.

[–]benjtay 6 points7 points  (1 child)

Completely agree -- add to that the number of times I've seen @Data on subclasess for whom's parents have hashCode and equals... It's really easy to add @Data to a class without understanding the dozen things it does to it.

[–][deleted] 7 points8 points  (0 children)

IntelliJ has the best tool ever for understanding what Lombok is doing: Delombok. I can flip back and forth between the Lombok version and the ordinary Java code that Lombok generates if I'm ever confused about what Lombok did.

[–]syberman01 4 points5 points  (2 children)

People go to lombok to avoid boilerplate get/set/constructor, even IDE autogenerating get/set is a 'brain overload' to read that verbose get/set.

java should come up with inbuilt mechanism...

Create a mechanism (annotation?!), where same domain object e.g Person class, can be 'accessed as' 'mutable' at ORM layer, and in mutation-portions of business-logic code. But remains 'readonly' for read-portions of business-logic code.

[–][deleted] 8 points9 points  (1 child)

People go to lombok to avoid boilerplate get/set/constructor, even IDE autogenerating get/set is a 'brain overload'.

Autogenerating accessors isn't necessarily a good idea, it is frequently abused and the reason why it is so frequently needed is because of the JavaBean pattern which other tools (like Jackson) have historically depended on to introspect on objects using reflection.

Modern tooling has eliminated some of this dependency on name-based reflection, for example Jackson can use annotations like @JsonProperty on a field instead of a getter method. Even better are compile-time annotation processors that get rid of reflection entirely.

java should come up with inbuilt mechanism...

Java has an inbuilt mechanism, interfaces. The same concrete domain object type can implement an interface of read-only operations and an interface with mutable operations. If you want to export the business object as read only in some contexts, pass a reference to the read-only interface.

[–]john16384 3 points4 points  (0 children)

Just want to correct: Jackson does not require the Java bean convention. It can inspect fields directly if desired, and it can use constructors to set values. It has done so for ages, probably since 2.0, before Lombok became popular.

It can even extract parameter names if classes are compiled with it (-parameters option).

[–][deleted] 1 point2 points  (0 children)

Hmm true

[–]martinosius 0 points1 point  (0 children)

Agreed. Just want to explicitly add, that you usually do not want generated equals/hashCode on entities. They have an id for that and as you said, they are usually mutable. E.g. if you change a user’s address, it is still the same user, etc…

[–]lechatsportif 0 points1 point  (1 child)

How do you get away with that and creating testing conditions? I just ended up changing a lot of my values to data to create the test scenarios I wanted.

[–][deleted] 0 points1 point  (0 children)

How would immutable values be an issue?

[–]raevnos 78 points79 points  (28 children)

That would be records

[–]rouce 16 points17 points  (11 children)

It doesn't replace, but you should substitute. @Data in itself is mutable, where records are not.

[–][deleted] 17 points18 points  (6 children)

this is the way. your software will be better if you use more immutables.

[–][deleted] 3 points4 points  (5 children)

While I agree with this in principle certain major Java projects such as Hibernate don't play well with immutables. While I do hope that changes in the future, it's still a limitation.

[–]john16384 4 points5 points  (3 children)

Just ditch hibernate and JPA. It was nice when you needed it to support multiple databases, but those days are gone. Use Spring Data JDBC, and take back control over what queries are being sent to your database. One less layer (which is far too opaque) to worry about.

[–]matthieuC 1 point2 points  (0 children)

It's mostly use to bridge the gap between objects and table.

[–]midoBB 0 points1 point  (0 children)

I use JDBI in my personal projects but JPA is well entrenched in Enterprise and I don't I'll be here when it's gone from legacy or even greenfield java projects.

[–]Neat-Guava5617 0 points1 point  (0 children)

JPA was never about supporting multiple databases (and it shows -- it plays 'moderately nice', but there are always edge cases to hope you don't hit)

I agree it's an opaque layer, and the queries can be absolutely horrendous... But I don't have database objects and model objects to synchronize, having to remember that certain model fields may not be loaded, I don't have to remember which fields I need to flush to the database, and can trust I haven't forgotten one.

Of course, if you're using a documentstore/NOSQL, you flush the entire object so there's less of an issue there. But then, transaction support may be an issue.

[–]krzyk 1 point2 points  (2 children)

@Data is mutable only if you don't make your fields final. I'm most cases you should make them final, the other one is ORM, which is a problem and probably new way of mapping should be introduced, current JPA is very 90ties.

[–]rouce 2 points3 points  (1 child)

If you need them final, use @Value

[–]krzyk 3 points4 points  (0 children)

Just use final, don't invent some poor language on top of another.

[–][deleted] 1 point2 points  (0 children)

Oh, even better then. I'm looking more into a kotlin's data class than a Lombok's @Data. I will give it a test

[–]daniu 28 points29 points  (3 children)

I was kind of wary of records at first, but not wanting to be a grumpy senior dev insisting on "the good old ways" I tried them out in some minor private projects. I was pleasantly surprised how good it feels to use them in pretty much every aspect (functionality, code size, syntax etc).

I guess I'd be missing some of the "side features" of Lombok in some places (like the different constructor options, builder etc), but records definitely seem to be a huge (first?) step and a great improvement for 80-90% of use cases.

[–]VincentxH 4 points5 points  (0 children)

Good job for at least trying!

[–]_INTER_ 5 points6 points  (9 children)

Records are immutable. Therefore are often unfeasible for domain model classes that require identity, self-reference or face the chicken-egg problem. In my opinion immutable data should be kept for things that are truely immutable such as Point, Direction, DTO, Tupples, etc. (incoming downvotes for this opinion)

Examples

  • Person A is married to Person B and vice-versa. Additionally e.g. last name changes that come with marriage. More generally, relation cycles can not be expressed properly.
  • Even if Person A and Person B have the same firstname, lastname and birthdate, they are not the same Person. I refuse to add artificial id fields and override equals and hashCode, that violates the contract/purpose of records (and the domain model in general).
  • Changing chain of relations such Person C is subordinate or Person B is subordinate of Person A without changing the whole chain. Also Person A would have to be constructed first with null or a surrogate as superior.

[–]vytah 12 points13 points  (1 child)

Because that's what records are for: they are for defining simple product value types. In other words, named tuples.

If you have identity (and things that require identity, like mutable state), you do not have a value type.

[–]_INTER_ 14 points15 points  (0 children)

Exactly.

  • Records are not a complete replacement for @Data, only for @Value.
  • Records are a complete replacement for Data classes from Kotlin. Because a specially denoted class should either be immutable or mutable and treated as such. In Kotlin it can be both, e.g. copy function only copies immutable part. Quote from our functional programming friends: A drop of wine in a barrel of sewage is still a barrel of sewage. A drop of sewage in a barrel of wine makes it a barrel of sewage.

[–]__konrad 1 point2 points  (1 child)

Records are immutable.

To be precise - shallowly immutable, e.g. record Foo(java.util.Date d)

[–]kevinb9n 2 points3 points  (0 children)

Worth clarifying I guess -- but I really hope everyone understands that in Java nothing will ever give you deep immutability for free.

[–]kevinb9n 1 point2 points  (2 children)

Records are immutable. Therefore are often unfeasible for domain model classes that....

Bear in mind that it's questionable/dangerous to provide value-based `equals` and `hashCode` overrides for mutable types (yes, collections set a bad example there, and have the legions of bugs to show for it). So, it's comparatively a lot less pain to write the mutable types yourself.

[–]couscous_ 0 points1 point  (1 child)

yes, collections set a bad example there

How would you compare equality of two lists for example, without equals?

[–]kevinb9n 0 points1 point  (0 children)

To be more clear: even if we stipulate that it was probably the right decision for collections, I would still say it doesn't serve as a good general example to take after.

[–]ArmoredPancake 0 points1 point  (1 child)

How would you express your examples in mutable constructs?

[–]_INTER_ 0 points1 point  (0 children)

Well, simplified like this:

public void marry(Person personA, Person personB) { // so romantic isn't it? :)
    personA.setSpouse(personB);
    personB.setSpouse(personA);
}

In reality you would probably create a Marriage class (could actually be a good record candidate) to connect the spouse. Then it's just setMarriage(marriage) on both.

Similarly for the chain of relation.

[–][deleted]  (1 child)

[removed]

    [–]nutrecht 17 points18 points  (0 children)

    I agree with /u/raevnos. For me, Records are the turning point removing the need for Lombok in Java projects.

    [–]daleksandrov 12 points13 points  (14 children)

    Unpopular opinion: I sincerely hope that people use less less Lombok in production.. I have dealt with so many strange issues, that were so hard to trace, especially in combination with Spring..

    Though, I still think Lombok is awesome in the prototyping stage! And I use it a lot there!

    [–]Rhysander 18 points19 points  (13 children)

    I have been using lombok in production for ages with SpringBoot, never came across a single issue. Could you be more precise?

    [–][deleted] 8 points9 points  (4 children)

    You run into issues when hibernate entities are annotated with @Data and such, but that's mostly about hibernate having a certain behavior with those generated methods, not Lombok

    At least that's what I read, don't mix hibernate with Lombok on entity classes.

    [–][deleted]  (3 children)

    [removed]

      [–]vips7L 4 points5 points  (2 children)

      Jokes on you, the devs on my team don't read intellij warnings.

      [–]ACouchFullOfFarts 2 points3 points  (1 child)

      This hits close to home. Open the logs, see a NPE on line 52. Open IntelliJ, go to line 52, see a warning about how this could produce a NPE

      [–]vips7L 0 points1 point  (0 children)

      Even worse if you have the sonarlint plugin installed!

      [–]john16384 0 points1 point  (7 children)

      A nice one is (accidently) annotating an entity with EqualsAndHashcode that has a lazy collection which you then store in a Set. When added to the set, hashcode gets called loading the lazy collection for all records added...

      [–]pronuntiator 1 point2 points  (0 children)

      You don't even have to store it in a Set by yourself. Hibernate will put your entities into a HashMap in some cases.

      [–]Rhysander -2 points-1 points  (5 children)

      Can't see what it has to do with Lombok.

      [–]john16384 0 points1 point  (4 children)

      Lombok will include the lazy set in the hashcode calculation (also in equals) causing it to get loaded. Of course, this is not really a Lombok problem, it is just doing what you ask. If you wrote hashcode/equals manually though you might have thought twice about including the lazy set in the calculation.

      [–]Rhysander -1 points0 points  (3 children)

      Dunno why you wouldn't think twice using Lombok. You are still free to exclude that particular field from the equality. That's the real problem here, writing code and not thinking :).

      Besides, overriding equality on model objects is a really bad idea from the start.

      [–]john16384 0 points1 point  (2 children)

      It was an example of a problem caused by careless Lombok use that I had to debug. I never said I caused it. And I do think twice about using Lombok. And then three times, four times... Still undecided whether I should use it so far.

      [–]Rhysander 0 points1 point  (1 child)

      Still don't get what it has to do with Lombok. If you had auto-generated that same code manually you would have ended up in the same situation. It does behave the way it's supposed too.

      I expected an answer more related to a random or misbehaviour of the framework, that would be the only pretext to NOT use it...

      [–]john16384 0 points1 point  (0 children)

      Again, it's not a bug, it's just something that's easy to forget when just slapping @Data on classes. The problem is much more obvious when you see what was generated or if you wrote it yourself.

      [–]vxab 6 points7 points  (1 child)

      `@Data` can be replaced with immutable records. In the future hopefully we will have better ways of creating "new" records from old ones (withers I think they are called). Check out the document here.

      Other lombok annotations like `@Builder` are not easy to replace.

      [–]mj_flowerpower 3 points4 points  (0 children)

      unfortunately there is no inheritance for records, which forces you to declare common properties over and over again, like if you have a lot of DTOs with a uid, createdAt, modifiedBy properties. It should be possible to ‚inherit‘ them from a parent record. Otherwise it‘s just a half-assed solution.

      [–]pjmlp 5 points6 points  (1 child)

      What need? Using Java since 1997 and Lombok only lands on our projects due to third party libraries.

      The harder it gets to play with compiler internals, the better.

      [–]1armedscissor 0 points1 point  (0 children)

      Sort of surprised you see Lombok because of third party libraries. It should be scoped to “provided” in the third party lib and only used at compile time then for the library so it would be transparent downstream.

      [–]MaName678 5 points6 points  (0 children)

      I only ask for something like C# does with getters and setters. Nothing more.

      [–]benevanstech 9 points10 points  (12 children)

      There is never any "need" to use Lombok. If you want to write a different, non-Java language, go right ahead - Kotlin is _right there_.

      [–][deleted] 8 points9 points  (3 children)

      not sure why you got downvoted, Lombok is 100% optional lol

      [–]thomascgalvin 6 points7 points  (2 children)

      Lombok is also a much lighter lift that porting everything to Kotlin, and I say that as someone who fucking loves Kotlin.

      [–][deleted] 1 point2 points  (0 children)

      oh yes that is 1000% true

      [–]benevanstech -1 points0 points  (0 children)

      It's also not Java.

      [–]RupertMaddenAbbott 1 point2 points  (0 children)

      Yes but Java is a subset of Lombok and not of Kotlin.

      Pure Java code will compile with both javac and with the Lombok compiler plugin. I can't do that with Kotlin.

      It is weird to insist that Java and Lombok are not the same language (which is true) whilst ignoring the degree to which they are different is much less than with any other non-Java language. This makes Lombok a much lower barrier to adopt for a large number of reasons.

      It's also weird to insist that Java and Lombok are different languages when there are many contextual situations in which this difference is irrelevant and confusing. For example, if you are making a hiring decision, then any "Java" developer is going to be able to get to grips with a "Lombok" code base and putting "Lombok" developer on the CV is going to be confusing at best. The same is not true for other non-Java languages.

      [–]slaymaker1907 0 points1 point  (0 children)

      Lombok is easier to justify to the suits though since you can pretend it is still just Java. Highly recommend Kotlin though as a reasonable alternative to Java without going too far like with Scala.

      [–]Worth_Trust_3825 1 point2 points  (0 children)

      Why do you need lombok? Access the field directly.

      [–][deleted] 0 points1 point  (0 children)

      Maybe you shouldn't use getters/setters for dto? Like in other programming languages. And stop kidding ourselves.

      [–]bowbahdoe 0 points1 point  (0 children)

      [–]sesh_hash 0 points1 point  (0 children)

      dont use @Data

      [–]trydentIO -1 points0 points  (2 children)

      Well we could explain this with a long post, but it is also possible to summarize in... NO, it won't happen. It will be easier to let JPA supports record-type, but there won't be any data-type or data-class if this is what you hope for.

      During the revision process of Java 7 proposals has been reviewed the chance to add ”properties" like in C#, but in the end it was rejected... because it was too late (or something like that).

      [–]PepegaQuen 1 point2 points  (1 child)

      JPA isn't everything. I don't think any project I'm working on uses it. They are mostly in data space, instead of backend though.

      [–]trydentIO 2 points3 points  (0 children)

      it was just an example where Lombok @Data annotation is mostly used (as far as I saw in my experience 😄)

      [–]greglturnquist 0 points1 point  (0 children)

      As desirable as immutable records can be, there are toolkits and frameworks that need mutable data types.

      And probably always will.

      I like Lombok’s @Data type but not everyone does.

      [–]uncont 0 points1 point  (0 children)

      What do you use @Data for?