14 ms·
Records Come to Java
- the_arun 7y agoInteresting.. why just stop at constructor, can't we introduce fluent/builder pattern as well?
- kelnos 7y agoI suppose there's nothing stopping you from writing your own builder to complement the record class, though of course part of the point here is to avoid additional boilerplate.
- mark_l_watson 7y agoThat looks like a good addition because anything that makes code more concise and easier to read is a win. Off topic, but I have been feeling nostalgic for Java, even though I now almost exclusively use Lisp languages and I am happy with that. In early Java years, Sun‘s main Java web site had a link to my site and I wrote a number of Java books. I was the number one search result for “Java consultant” for about a decade; Java was good for my career.
- rumanator 7y ago> That looks like a good addition because anything that makes code more concise and easier to read is a win. Arguably, as Java already had Lombok for years, this does not add anything really new.
- mark_l_watson 7y ago+1 for mentioning Lombok. I used to use it a long time ago.
- cle 7y agoForcing usage of constructors is hardly more readable IMO. Which bug is easier to spot? cat = new Cat(1, 2, 4); cat = Cat.builder().eyes(1).legs(2).heads(4).build(); Additionally, from the examples in that post, records aren't valid JavaBeans either, so they can't be used with the myriad of existing tools/libraries that expect beans. From my first impression, it looks like records don't really address many of the reasons we use Lombok and its ilk, so many of us will just ignore this feature. Or do like I've done in the past couple of years, and just ignore Java altogether.
- skocznymroczny 7y agoJava could really used keyword arguments as a replacement of the Builder pattern: cat = new Cat(eyes=1, heads=2, legs=4);
- rooam-dev 7y agoToo much for small classes/records imho e.g. `new Name("First", "Last")` vs. `new Name(first="First", last="Last")` Also, IDEs can help with parameter hints when is needed.
- deleted 7y ago[deleted]
- deepsun 7y agoThey can be optional, like in Kotlin -- you can give name to only some (after unnamed ones).
- rooam-dev 7y agoI think it's perfect for parameter objects
- kelnos 7y agoI expect the reason that they didn't want them to be valid beans is because then they'd lose immutability.
- Rebelgecko 7y agoIf you're using a decent IDE, that bug is just as easy to spot when using a constructor. My IDE adds the var names in front of the values, so when I create a new Cat(1,2,4) it actually looks like this: new Cat(eyes:1, heads:2, legs:4);
- electrum 7y agoThis is how they look in IntelliJ IDEA: https://www.jetbrains.com/help/idea/viewing-reference-information.html#inlay-hints https://www.jetbrains.com/help/idea/viewing-reference-inform...
- d_t_w 7y agoWhich Lisp flavour do you use Mark? I worked in Java for 15 years before moving to Clojure a decade ago. I find it superbly powerful to be able to leverage the JVM and the Browser with the same language.
- mark_l_watson 7y agoI mostly use Common Lisp, Racket, and periodically like to try the latest versions of Gambit, Chez, and Guile. I used to have two customers who paid me to use Clojure on their projects, I liked Clojure (donated in the early days) but fast natively compiled tools like SBCL, etc. feel better to me. You have found a good home: Clojure and the JVM ecosystem is good for a wide range of uses.
- thu2111 7y agoHave you tried Clojure with the GraalVM native-image? I am curious to what extent native-image can delete the overhead of Clojure and make fast natively compiled binaries.
- mark_l_watson 7y agoNo, I haven’t tried it. I retired last year and the limited time I spend on tech is now persuing personal research on hybrid symbolic and deep learning systems; I use the Hy language ( hylang) for this because it wraps Python (for deep learning) in a reasonable Lisp language with a Clojure-like syntax. I am also writing a book on Hy. Otherwise Common Lisp and Racket(and a little Haskell) suit my hacking needs.
- thomascgalvin 7y agoI feel like Java is making great strides lately, but that we're also getting slightly lesser versions of capabilities available in other languages. Records, for example, are directly inspired by Kotlin's data classes, and function almost identically... except that Kotlin allows you to have mutable or immutable fields in a data class, and automatically provides a `copy` method (similar to Lombok's `wither`) that allows you to use a builder pattern. This difference can be very important. For example, I don't see how a Record class could be used with most JPA-style ORMs, which immediately rules out Records for many of the most common use cases.
- chrisseaton 7y agoJava is maintaining significant backwards compatibility. Kotlin does not have to do this. More constraints leads to a different design.
- snuxoll 7y agoMakes absolutely no difference here, Kotlin data classes are just regular Java classes like records are - the compiler implements these methods automatically. Oracle literally did the minimum effort required here, Java Records don't even generate bean-compliant getters FFS.
- nobleach 7y agoI'd equate Kotlin's data clases to Lombok's @data annotation.
- doctor_eval 7y agoAgreed, the declaration syntax is awful and the generated methods aren’t idiomatic. That saidC I don’t even understand the point of generating getters in this case. The fields could just be declared public final. Java’s obsession with wrapping fields in methods has never made sense to me.
- erik_seaberg 7y ago
- bozoUser 7y agoThis is akin to case classes of Scala such a welcoming change to the Java world. Apart from being a functional language one of main draws to Scala is the ease with which one can get rid of the boilerplate code. So kudos to the Java team!
- The_rationalist 7y agoBut case classes are also algebraic data types so Java is not yet as great.
- thrower123 7y agoI am continually surprised that Java hasn't adopted C#'s property syntax. Having to write backing fields and your own getters and setters takes me back.
- peterashford 7y agoAs someone who codes C# every day, I hate properties with a passion. Its my least favourite feature of c#
- thrower123 7y agoWhy?
- rosybox 7y agoSo Java is basically getting structs. Seems awesome.
- bassman9000 7y agoSo Beans are back?
- frankosaurus 7y agoI'm curious if this will make AutoValue obsolete: https://github.com/google/auto/blob/master/value/userguide/index.md https://github.com/google/auto/blob/master/value/userguide/i...
- apta 7y agoIt should.
- djsumdog 7y agoJava finally gets what's been available in Scala (case classes), C# (structs or maybe properties), Kotlin (data classes) and others for a very long time .. so long we already have tools like Immutables and Lombok to get past this really dumb limitation in Java. I was really surprised to learn that the Java compiler/VM, when it sees the patterns "somevar" and functions "getSomeVar" and "setSomeVar" doing purely getter/setter stuff, removes the actual function calls and silently changes them in the background to field lookups for speed. If you attach a debugger or memory profiler, is changes them back to functions and slows them down. I'm not sure if this is still true; saw an example of this at a meetup back in the Java 8 days.
- apta 7y ago> C# (structs or maybe properties) Those are not directly analogous to records. As a matter of fact, C# is getting records as well: https://blog.cdemi.io/whats-coming-in-c-8-0-records/ https://blog.cdemi.io/whats-coming-in-c-8-0-records/
- statictype 7y agoThis looks like syntactic sugar for comcepts that already exist? I think in Java’s case, its only mow getting struct-like semantics edit I may be wrong. It could be a purely syntax level feature in Java as well.
- paulddraper 7y agoJava records are syntax sugar. Value semantics are proposed in JEP 169 [1] and are part of Project Valhalla. [2] [1] https://openjdk.java.net/jeps/169 https://openjdk.java.net/jeps/169 [2] https://wiki.openjdk.java.net/display/valhalla/Main https://wiki.openjdk.java.net/display/valhalla/Main
- to11mtm 7y agoC# has had records slated for years without delivery, alas. Thankfully we do finally have a version in testing. In any case to answer GP, structs should not be considered a valid substitute for records, they are 'value types' so passing them around means you copy all the data every time you pass (i.e. lots of allocations if the record is larger than 1 or 2 fields) Properties get you 'closer'. You can do some code Which is a good bit less than Java by default (i.e. not Lombok or similar) F# does have Records, and technically there's nothing stopping you from declaring your records in F# and importing them into C#... but that may not be ideal. I don't think you get the full benefits (like copying a record with specific properties changed)
- nitrogen 7y agoFrom the first example: private final double price; private final LocalDateTime sentAt; Can't help but notice that they won't be able to trade at exact decimal amounts whose fractions aren't sums of powers of 1/2. Also wouldn't Instant be better for a timestamp?
- pintxo 7y agoProduction code likely should use neither double for currency nor LocalDateTime for a point in time. But for the purpose of the article, which is about Records being a new type of Java class, this is kind of irrelevant.
- nitrogen 7y agoI think it's kind of a problem if examples of how to write Java are full of bad practices.
- barnaclejive 7y agoExamples are usually bad production code. The sky is blue.
- pgwhalen 7y agoFor what it's worth, I work at a place that would write a similar domain object, and we would use both of those and not feel bad about it. The FUD on money as a double is way overblown in my opinion - it only becomes a bad idea once you have to do precise math with the it (which is admittedly a lot of the time when you're dealing with money, but surprisingly rarely in some domains).
- bobthepanda 7y agoCan anyone explain to me what the benefit of this is over Lombok annotations? I like the Lombok builder syntax which this seems to lack.
- kelnos 7y agoComing from scala, I consider this an essential language feature, and I don't want to have to depend on a third-party library / compiler plugin / annotation processor to get it. This should be standardized by the language.
- iSnow 7y agoIt's a language feature, so esp. library developers would pick it up over Lombok to reduce their dependency graph. That said, I also like Lombok's annotation style more, I can pick & filter which members are relevant for `toString()` and `equals()`
- peeters 7y agoBesides just the convenience of it not being a dependency, this will allow other language-level features like pattern matching and object deconstruction in the future. Basically anything that requires the language to have a tuple/named tuple type.
- bobthepanda 7y ago> tuple/named tuple type As someone not familiar with using those, can you ELI5 what those are and what their benefits are?
- peeters 7y agoA tuple is the generic term for a pair, triple, etc. It's a collection of data of heterogeneous types, unlike an array which is homogeneous. When the members are unnamed, the data is indexed by its position like an array. E.g. a method could return an unnamed tuple ("foo", 7) which might have type (String, int). In some languages like ML, having tuples is such a fundamental part of the language that things like functions always act on a single argument which is actually just a tuple. So max(5, 5) is a function that takes a tuple of two ints as its parameter. When you have tuples as a first-class concept, you can start to do some interesting shortcuts like: (b, a) = (a, b); // swap two variables with no intermediate or String (first, last) = name; This is called deconstruction, where you extract a tuple into variables representing its members. This is common in a ton of languages (Typescript, C#, ML, Scala, Python, etc). It also forms a cornerstone of pattern matching expressions in some languages (Scala, Haskell) where you can create a condition and simultaneously assign the data you care about to variables that can then be used in that branch. Scala pseudocode: sum(list: ConsList<int>) { return list match { Empty(): 0; Node(first, rest): first + sum(rest); } }
- jnwatson 7y agoPython recently added the same thing in 3.7. They call them data classes. https://docs.python.org/3/library/dataclasses.html https://docs.python.org/3/library/dataclasses.html
- dfox 7y agoPython has had namedtuples for a long time, which is similar idea implemented less cleanly (in fact the implementation is horrible hack because of some rare corner cases) and without dependence on Py3 annotation syntax
- dehrmann 7y ago> less cleanly Python has a number of these warts. Same with classes and type annotations.
- fourseventy 7y agowe are on java 14 already wtf?? I really don't like the new frantic release cycle. Cassandra is still stuck on Java 8...
- lostcolony 7y agoEverybody is still stuck on Java 8.
- hinkley 7y agoI know of a project with substantial GC problems that's stuck on Java 7. I just roll my eyes at this point when I hear about it.
- hinkley 7y agoI look forward, with great enthusiasm, to the day when we figure out what the optimal release cycle is, where you keep the bulk of your users in space between bleeding edge and falling off the upgrade bandwagon entirely due to being 'so far behind'. But today is not that day.
- imtringued 7y agoJava will have it's Python 3 moment... I have already upgraded to Java 11 without too many issues however.
- kllrnohj 7y agoThere's only been one "real" release since Java 8, which was Java 11. By which I mean those are the only LTS releases since the shift to frequent releases. The rest have been glorified betas, sometimes with major compiler bugs included (such as https://dzone.com/articles/jdk-91011-side-effects-from-on-java-string https://dzone.com/articles/jdk-91011-side-effects-from-on-ja... ). They have very little support and a super tiny maintenance window. I wouldn't expect any large or stable library/project to use anything but the LTS releases as a result. It looks like Java 17 is the next planned LTS release, which is quite a ways off.
- taftster 7y ago
- davewritescode 7y agoThe best thing about this is VM support for records. This means that all the other JVM languages that implement similar features can potentially leverage the underlying support for records to make better implementations themselves. I for example, don’t immediately see any reason kotlin can’t use this to make a more efficient implementation of data classes.
- kllrnohj 7y agoWhat VM support? Records are a Java compiler feature. It just auto-generates all the boilerplate at compile time, there doesn't seem to be any bytecode or VM support for this feature? It seems functionally identical at the VM/bytecode level to what Kotlin's data classes already do.
- davewritescode 7y agoMy mistake, I thought this was more akin to value types proposed in this JEP https://openjdk.java.net/jeps/169 https://openjdk.java.net/jeps/169 This is disappointing.
- pron 7y agoRecords are meant to play nicely with inline types, i.e. when inline types land, you'll be able to declare inline records.
- morelisp 7y agoThere's a lot to like about this, but I laughed at trying to cast nominal typing as a design choice in typing per se rather than e.g. a tool to make the VM's loader simpler. It gives away the lie just two paragraphs after explaining it: > This choice was partially driven by a key design idea in Java’s type system known as nominal typing, which is the idea that... each type has a name that should be meaningful to humans... the compiler still produced two different anonymous classes, $0 and $1... Names extremely meaningful to humans!
- dvt 7y agoThe article doesn't really touch on how this plays with generics, if at all. Anyone know if we can do: public record MktOrder(T currency, int amount)<T> { } or public record MktOrder<T>(T currency, int amount) { } Would be a bummer if it breaks the idiomatic (and canonical) way of working with generics. In any case, it's a much needed and welcome feature.
- smarks 7y agoYes, records work with generics pretty much the same way they work with classes. You can do public record MktOrder<T>(T currency, int amount) { } var m1 = new MktOrder<USD>(usd, amount);
- grizzles 7y agoA missed opportunity? If the struct graph is all records or primitives, the compiler should include a toJson(), fromJson() in the sealed class. It could be done with classes too, but if it was records/prims only, then it could be done without using reflection.
- specialist 7y agoI guess I was hoping the same end result would have been reached by different means. I would like to see: new non canonical java.lang.BaseObject, which implements nothing. new interfaces for .toString(), .equals( that ), and .hashCode(). Canonical java.lang.Object implement these new interfaces. Any code wanting struct, data, record style objects would subclass BaseObject. The JVM would auto generate missing .toString(), .equals( that ), and .hashCode() as needed. Like when you add a data class to a HashMap. New syntax for properties, to eliminate need for setter/getter boilerplate. New syntactic sugar for the static initializer constructor trick, to eliminate constructor boilerplate. Basically make new MyDataObject() {{ a = 1; b = "abc" }} look prettier. -- Best as I can tell, only the last two would require language changes.
- flerchin 7y agoSo basically Lombok's @Data annotation with extra steps.
- krzyk 7y agoWhy more steps? You even have fewer keystrokes in case of record. I'm looking forward to ditching lombok finally.
- juped 7y agoYes, it's absolutely not innovation - but this is actually really cool in the context of Java, which was never an innovator.
- dehrmann 7y agoI'm pretty excited for this. I've seen Java developers jump through hoops so they wouldn't have to implement a new class when a Python developer would use a NamedTuple and not think twice. And yes, there are codegen options (Autovalue, Lombok, etc.), but the build process is a little clunkier, and IDE support isn't great.