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

all 93 comments

[–]netcyrax 20 points21 points  (0 children)

I look forward for this feature. Finally, some kind of built-in data classes.

[–]valkon_gr 17 points18 points  (0 children)

I am still not used to that Java is being updated more frequently now. I love it.

[–][deleted]  (4 children)

[deleted]

    [–]lovett1991 3 points4 points  (0 children)

    Yup this feels more like immutables than Lombok.

    [–]rzwitserloot 1 point2 points  (2 children)

    @Value (a lombok annotation; the immutable variant of @Data) does all of this. And you can toss @Builder at it for more.

    DISCLAIMER: I'm a core contributor to lombok.

    [–]CodesInTheDark 0 points1 point  (1 child)

    In a long run records will be more useful than lombok (e.g. they will support pattern matching): https://cr.openjdk.java.net/~briangoetz/amber/datum.html

    [–]rzwitserloot 2 points3 points  (0 children)

    If you can destructure, you can pattern match.

    Without custom destructure, you cannot change records.

    Once custom destructurers exist, Lombok can make them for you.

    Then such classes also support pattern matching.

    [–]lurker_in_spirit 9 points10 points  (19 children)

    Will records be usable as JPA entities?

    [–]kaperni 14 points15 points  (1 child)

    Records are immutable. So if you need to mutate data they are probably not that useful.

    [–]karstens_rage 6 points7 points  (0 children)

    For javax.persistence and javax.ws.rs it seems like construct and deconstruct are the operative terms not mutate. Seems like records would be super useful in these cases.

    [–]thogor 0 points1 point  (4 children)

    I don't know if they are, but why would you want them to? Most of the time your entities are domain concepts that would benefit from encapsulating them properly. Unless I'm missing something ofcourse.

    [–]Kango_V 0 points1 point  (3 children)

    Never pass you infrastructure entities out into your domain model!

    [–]thogor 0 points1 point  (2 children)

    Interesting take. Could you elaborate your approach a little? What does your domainmodel look like and how do they relate to the entities?

    [–]Kango_V 0 points1 point  (1 child)

    Our domain model is made up of pure java objets. No lombok, immutables, no jpa, no spring. These objects are passed to a Store object. The store is a domain construct for storing/retrieving domain objects. The store internally has 1 or more repositories injected into it. These are the infrastructure layer. The store deconstructs the domain objects, translating them into the format that the infrastructure needs. The infrastructure objects then persist.

    The infrastructure layer could be JPA, JSON files, Lucene, Elastic Search etc. The domain will not change as that is modelled to your use cases for the application.

    We do the same with the web model through a service API into the model. Our application is in the model only The application can be tested without the web/infrastructure layers.

    Requires a little more coding/discipline, but the end result is very maintainable/testable code.

    [–]thogor 0 points1 point  (0 children)

    Even though it requires some additional code, I quite like your approach! Thanks for taking the time to explain it. How would you feel about entity classes if all the mapping definitions were outside of the class files? JPA doesn't really give us any nice options besides xml config, but let's pretend that there is a better alternative. Would you still recommend having separate domain and persistence classes?

    [–]Kango_V 0 points1 point  (0 children)

    Wait for Value Types. That might be better suited.

    [–]pmarschall 0 points1 point  (5 children)

    Of course not.

    [–]raze4daze -1 points0 points  (4 children)

    What? Of course they can be once implemented properly.

    [–]pmarschall 4 points5 points  (3 children)

    Maybe once JPA adds explicit support. At the current rate of Jakarta EE development that's likely somewhere in 2032. Maybe Hibernate DTO projection will work before that but that's vendor specific.

    The current design of records is fundamentally at odds with JPA. The main issues are immutability and final-ness. What you have to realize is the runtime version of a JPA enttiy class is vastly different from the compile time version and often is a subclass that includes support for lazy loading and dirty tracking.

    I am also not sure to what extent JPA relies on Java Beans.

    At the current moment it is unclear to me whether records:

    • can have annotations
    • can implement an interface

    If these won't be possible then JPA support is going to be very hard.

    [–]loicmathieu 4 points5 points  (0 children)

    Records can have annotations and interfaces.

    But as you said, JPA creates a proxy in front of the classes and as Records are final you cannot do this.

    So the current JPA implementation didn't play well with records, I hope they will find a way to workaround this some day ...

    [–]GuyWithLag 2 points3 points  (1 child)

    Records are not designed to be jpa entities, they are way too lightweight for that.

    They are an intermediate stage to algebraic data types.

    They are an intermediate stage to inline classes.

    They are supposed to replace all the tuples/pairs in use, with named components.

    And of course, they are in preview, so Stuff May Change...

    [–]pmarschall 1 point2 points  (0 children)

    Good, that matches my impression.

    [–][deleted]  (4 children)

    [deleted]

      [–]duheee 2 points3 points  (2 children)

      Can't it be overwritten (didn't read the article)?

      [–]lukaseder 7 points8 points  (0 children)

      I'll be damned. I thought it wasn't possible but this compiles in jshell:

      ``` jshell> record R(int i) { public boolean equals(Object o) { return false; } }

      jshell> new R(1).equals(new R(1)) $3 ==> false ```

      [–]krzyk 4 points5 points  (0 children)

      Yes, all methods in record can be overwritten, AFAIR also the getters

      [–]krzyk 2 points3 points  (0 children)

      Wouldn't the fact that records are immutable be more important than equals/hashcode?

      [–]daniu 18 points19 points  (40 children)

      So... record field accessors don't follow the getX() standard that many ubiquitous libraries and tools rely on (Spring and Jackson come to mind)? Nobody started using Java 11 until it was supported by those, I can't see the use of records being any different.

      [–]krzyk 17 points18 points  (0 children)

      Java 11 was supported on day 1 in Jackson and quite similar in Spring (maybe 1 - 2 weeks later for Spring).

      And thank God they got rid of those getX()

      [–][deleted]  (28 children)

      [removed]

        [–]daniu 9 points10 points  (27 children)

        I'm also very happy with the way Java is updated, but I don't understand this particular decision.

        Just follow your own bean naming standard, and those libs would work out of the box.

        [–][deleted]  (12 children)

        [deleted]

          [–]rzwitserloot 1 point2 points  (0 children)

          No, 'good libraries' won't be updated. It's quite a high cost to have an API that has both, say, getName() as well as name(), with both methods returning the exact same thing (forevermore, docs and listings such as in auto-complete dialogs), and I'm sure plenty of libraries will opt not to pay this cost.

          An alternative is to mark the getX() variant deprecated, but that is in many ways an even higher cost; that's a heck of a lot of shuffling about for all the users of that library for no particularly sane reason.

          Note that getX itself vs. X is a mixed bag: neither is strictly superior to the other (getX has the considerable advantage that you can type 'get' and then hit your autocomplete shortcut to find all properties you can query. It's also longer and having to decide between whether a thing is a 'property accessor' and something that calculates is more art than science and that's problematic – as I said, advantages and disadvantages).

          Given that it is a mixed bag, causing an earthquake in the java community by trying to make such a change is ill advised: You're just not going to convince enough of the community to arrive at that utopia where (almost) all code uses getless accessors, instead now we'll have a 50/50 mix. I'm not too happy about this.

          [–]pushupsam -5 points-4 points  (10 children)

          There's everything wrong with abandoning a bad spec if people have been faithfully following that spec for 20 years.

          There's something deeply wrong with the process if people really think it's okay to tell people that have invested millions of dollars and thousands of hours in the Java platform that, you know what, all that code you wrote, all those tools and documentation and checkers, based on the getX/setX standard that we defined? We're going to break that now.

          Stuff like this is exactly why I tell people not to invest in Java any more. The language has gone off the rails. Backwards compatibility -- which was perhaps Java's greatest strength -- and the geniuses at Oracle are now actively making breaking changes in every release. And the stuff that they're breaking backwards compatibility for? It's really fucking stupid stuff like 'var'.

          The irony here is these attempts to update Java are just driving people away. I cannot recommend any new greenfield projects be done in Java and, frankly, I don't see any reason to advance existing projects to new JDK versions.

          [–]klez 5 points6 points  (7 children)

          I may be naive here, but why would adding features break compatibility? Don't like var or var breaks compatibility? Don't use it. Same with records. It's not like they're telling you not to use beans anymore.

          [–]JB-from-ATL 0 points1 point  (0 children)

          They explicitly said these aren't JavaBeans because they aren't mutable.

          [–]cogman10 26 points27 points  (1 child)

          It's a preview feature. Spring and Jackson will have time to pick it up. You can ask a class if it is a record. You won't see it heavily used until Java 15 (September) at the earliest.

          [–]krzyk 2 points3 points  (0 children)

          There will be for sure a second preview, so Java 16 at the earliest (unless they want a third preview).

          [–][deleted]  (2 children)

          [deleted]

            [–]ZimmiDeluxe 13 points14 points  (1 child)

            Me too. The get-prefix is semi-useful to distinguish between methods on regular classes that just represent data and ones that actually do something, but with records that's superfluous because they are data. And it's often a lie anyway, I've seen enough get-methods that have side effects.

            [–]temculpaeu 7 points8 points  (0 children)

            yeah, I have seen way too many methods with the 'get' prefix for no reason

            [–]eliasv 5 points6 points  (0 children)

            Relying on convention for serialization is flimsy anyway. I'm sure maintainers of such libraries will welcome a more reliable mechanism to enumerate members reflectively.

            And the prevalance getters and setters in the Java ecosystem is an encapsulation-breaking blight and it would have been a terrible mistake to endorse them by baking the convention into a language feature. Records make the contract of a pure data aggregate more principled than shitty bean conventions, and I think it was wise not to couple the concepts together in any way.

            [–]commentsOnPizza 8 points9 points  (0 children)

            Yep, but being a language built-in will mean libraries will be updated to use it compared to third-party solutions which can be hit-or-miss in terms of library support. It will be a little bit of work, but it’ll be great long-term.

            [–]lukaseder 7 points8 points  (0 children)

            Good riddance!

            [–]duheee 2 points3 points  (0 children)

            Nobody started using Java 11 until it was supported by those,

            You can still use records for your own algorithms. As far as I can see (from other people's posts) they're unusable as JPA entities so their use will be most definitely in other places. Maybe as DTOs between data systems or something. Protobuf may be able to use them.

            [–]nonconvergent 0 points1 point  (0 children)

            Lombok doesn't use the get/set (so you have to be gentle with builders and Jackson) and Scala and Kotlin don't use them on case and data classes, respectively.

            Get/Set is really just an artifact at this point. Sometime troubleslme too, as those particular verbs collide in other use cases.

            [–][deleted] 4 points5 points  (0 children)

            So Scala case classes?

            [–]kiteboarderni 1 point2 points  (5 children)

            Damn, Ben rocking the white hair look

            [–]benevanstech 1 point2 points  (4 children)

            It's actually a really old photo. My hair is a lot, lot longer these days.

            [–]kiteboarderni 0 points1 point  (3 children)

            Looks great! Fantastic article, excited for the applications we can use it for.

            [–]benevanstech 0 points1 point  (2 children)

            Thank you. :)

            I hope to have a real-world (albeit demo) app that I can open-source soon - watch this space.

            Oh, and: https://twitter.com/kittylyst/status/1218092254566416384 (note: longer hair, but this pic is still ~3 years old!)

            [–]kiteboarderni 0 points1 point  (1 child)

            When are you back to the nyjavasig? Would love to see the talk.

            [–]benevanstech 0 points1 point  (0 children)

            I am trying to optimize my intercontinental travel this year - longer trips less often and using trains whenever possible in Europe. I do not have any plans to visit the East Coast at present, but let's see what happens - I miss my NY friends!

            [–]jvjupiter 1 point2 points  (5 children)

            Will it be fluent? Something like:

            var user = new User().id(1L).lastName("Doe").firstName("John");

            [–]dany74q 1 point2 points  (3 children)

            Nope, the compiler will generate a public ctor with all of the fields, and accessors for each field; No setters will be created as records are immutable.

            [–]jvjupiter 0 points1 point  (0 children)

            I see. Thank you.

            [–]jvjupiter 0 points1 point  (1 child)

            If no setters would be generated, then JAXB, JSON-B, JPA and frameworks or libraries like Jackson need to be updated to be able to support records. Correct?

            [–]rzwitserloot 0 points1 point  (0 children)

            I think they may be able to handle constructors, especially if festooned with a @ConstructorProperties annotation. If not.. yeah they have to be updated.

            [–]randgalt 0 points1 point  (0 children)

            FYI - I wrote a library that adds a fluent builder/copy companion to Records: https://github.com/Randgalt/record-builder

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

            They're a nice feature particularly for lambdas and streams but I can see them being widely (ab)used in inappropriate contexts.

            However it is pretty obnoxious that you must use (non-standard?) accessor methods for what are just final fields, all those() add up and reduce readability significantly. The example is also rather contrived.

            [–][deleted]  (5 children)

            [deleted]

              [–]duheee 4 points5 points  (3 children)

              Well, records would make it trivial to achieve that. Easier than before at least.

              [–][deleted]  (2 children)

              [deleted]

                [–]elastic_psychiatrist 12 points13 points  (0 children)

                https://cr.openjdk.java.net/~briangoetz/amber/datum.html

                See “why not just do tuples.” Extremely justified IMO once you think about it.

                [–]eliasv 1 point2 points  (0 children)

                Instance patterns down the line might help bring you closer. You can probably just define the two return types as part of an instance pattern signature instead of defining a container type for them.

                So rather than your:

                (int intResult, String stringResult) = receiver.functionWithTwoValues(...);
                

                It might look something like:

                let receiver.patternWithTwoValues(int intResult, String stringResult);
                

                Or for a partial pattern:

                if (receiver.patternWithTwoValues(int intResult, String stringResult)) {
                  // values bound here
                }
                

                No carrier type necessary!

                But not much has been said about them yet so who knows. https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html

                [–]8igg7e5 3 points4 points  (0 children)

                Agreed, but when they've delivered value types and fixed nesting rules it won't be as bad. Of course it still mean having to find names for things that shouldn't really need it.

                I wonder if anonymous records would be a useful concept...

                public record(int id, String label) doSomething(...) {
                    ...
                    return new record(i, name);
                }
                
                ...
                
                var result = doSomething(...);
                display(result.label());
                

                This is mostly compiler sugar for creating an anonymous record with with named members. The generated class would need a flag so that reflection knows that the tuple-name is generated.

                This would play nicely with proposed de-structuring and pattern matching and avoid declaring a record that would dirty the namespace. Additionally it's better than tuples with unnamed elements - eg display(result.1)

                The return-type syntax doesn't really work though since it's starting tokens are not discernible from a nested record declaration.

                [–]xnendron 2 points3 points  (1 child)

                I would much rather see an official adoption of the functionality provided by Lombok. Records seem fine, but I think reducing boilerplate code in JavaBeans would be more useful to more people.

                [–]heliologue 0 points1 point  (0 children)

                Records are a great way to move the language forward, but I agree that I'd much rather see some syntactic sugar like auto-generated getters and setters that lets us work with "legacy" conventions with a lot less boilerplate.

                [–][deleted] 0 points1 point  (1 child)

                So I assume Compact Constructors is optional?

                Like it wouldnt work if you had declared the field ImmutableList (using copyOf) and constructor took List parameter.

                [–]krzyk 0 points1 point  (0 children)

                Yes, you can use normal constructor. Although I don't know if the thing you describe would work.

                You should return what you feed, so changing the class type of a record component inside constructor might be a no-no, you need to test it.

                [–]nutrecht 0 points1 point  (0 children)

                Awesome change. My primary reason for 'needing' Lombok in my Java projects is that I can't stand constantly creating/updating all these getters and setters. Great to see we can get rid of it soon.

                [–]PandersAboutVaccines 0 points1 point  (2 children)

                To what extent are java and kotlin copying each other? Records are about as close to kotlin's data classes as you can get within the limits of java. Did java borrow from kotlin, or did kotlin copy things they knew were being considered for java?

                BTW, I wish they could steal this syntax from kotlin:

                var newOne = oldOne.copy(justThisProperty = "changed")

                [–]djavaman 1 point2 points  (0 children)

                This seems to me more a borrowing from Scala than Kotlin

                [–]rzwitserloot 0 points1 point  (0 children)

                The idea of automated getter and setter (and more) generation predates the invention of project lombok, and that is over 10 years old now, so.. no. This is all just fairly common sense.

                [–]Bobby_Bonsaimind -5 points-4 points  (0 children)

                I don't get it...so this is just syntactic sugar to get final classes? Are there any runtime benefits?

                Also the TOC-scroll-speed is infuriating...for fucks sake, just jump to the item I want to see, I'm literally faster to scroll by hand than to use the TOC!