14 ms·
Java 14 Feature Spotlight: Records
- dangerlibrary 7y agoThese seem extremely similar to Scala's case classes. In my experience, case classes are often the feature that sells people on moving over to Scala from Java (the other being exhaustive pattern matching warnings from the compiler). Fingers crossed Java never implements the `implicit` keyword...
- McDev 7y agoAnd Kotlin data classes: https://kotlinlang.org/docs/reference/data-classes.html https://kotlinlang.org/docs/reference/data-classes.html
- q3k 7y ago... all of these are likely inspired by the extremely powerful namesake of Java Records: Haskell Records [1]. [1] - http://learnyouahaskell.com/making-our-own-types-and-typeclasses#record-syntax http://learnyouahaskell.com/making-our-own-types-and-typecla...
- taeric 7y agoI'm going on a limb and saying the namesake goes back to pascal. If not farther.
- kryptiskt 7y agoThe original COBOL had them, maybe it's not surprising, it's a pretty natural metaphor for a business language. Fortran, Lisp and Algol 60 didn't have them. So I guess we can thank the bean counters for the idea.
- tombert 7y agoGonna take an unpopular opinion here, but I completely hate Haskell records. I'm sure that there's some kind of purity to them, but the fact that a two records can't share field names ends up making them incredibly difficult to use for anything practical. Granted, using the makeFields function with Lenses allows you to kind of avoid the clashing, but that in itself feels like somewhat of a hack.
- teruakohatu 7y agoFor those of us who don't do haskell, can you give us an example of what does not work and then work around?
- dwohnitmok 7y agoFor an example of clashing: data Person = Person { id :: Int, name :: Text } data Account = Account { id :: Int, # Oh no! Name clash amount :: Int } This is because `id` isn't really a "field," among other thing it's a generated function so that id :: Amount -> Int id (Amount actualId _) = actualId This particular case is compounded by the fact that `id` is already the name of the identity function in Haskell's standard library (one reason why many projects that roll their own standard library rename `id` to `identity`). For the partiality: data AllowedItems = Person { id :: Int, name :: Text} | JustANormalInteger Int name (JustANormalInteger 5) # Blows up at runtime That is name has the type AllowedItems -> Text, even though JustANormalInteger 5 is also a member of AllowedItems The workaround for clashing is to prefix your fields with your type name (luckily Haskell still has module namespaces so you don't need a globally unique name, just one unique for your module). The workaround for partiality is to disallow a type with a record to be anything other than that record (e.g. disallow JustANormalInteger or require that Person {...} must first be an independent type before it can be used in AllowedItems). The talk of lenses is a way to generalize the notion of field accessors and talk about the "path" generated by a series of field accessors in a first class way. E.g. you might imagine that in a standard OO language myClass.field0.subField0.subSubField0 could have a stand-alone concept of `field0.subField0.subsubField0` as a path through `myClass` that you could then use either to get a value (get(field0.subField0.subSubField0, myClass)) or set a value (set(field0.subField0.subSubField0, myClass, newValue)). Moreover, if you can talk about the paths through a class in a first-class way, what's to say that this path must actually correspond to a true field in a class? It could just be anything "field-like." For example: lens = integer.lastDigit.leastSignificantBit set(345, lens, 0) // Yields 344 even though the individual digits of myInteger aren't actually generally truly fields in any OO language nor are the individual bits of a number. Because you have now removed the need for true "fields," lenses are one strategy to get around the hackiness of fields in Haskell and in fact in many ways represent an advance in expressiveness over "true" fields (although they suffer from not really being integrated into the language in the way faux-fields are currently in Haskell).
- deleted 7y ago[deleted]
- pron 7y agoKotlin's data classes are quite different from the other three (Scala's case classes, Java's records and Haskell records), and those three are inspired by ML's records, but the name "record" predates even ML.
- harryh 7y agoThe implicit keyword is extremely powerful when used appropriately and not something that should just be shunned completely. My favorite usage is probably in a typesafe Query engine that several people[1] built at Foursquare that is a lot better than what you can find in the java world. Venue.where(_.closed eqs false) .orderAsc(_.popularity) .limit(10) .modify(_.closed setTo true) .updateMulti It allows for all sorts of typesafe queries that will fail at compile time if things are malformed including stuff like failing to query on an index. https://github.com/foursquare/rogue https://github.com/foursquare/rogue 1. Hi Jorge, Liszka & Neil!
- dangerlibrary 7y agoI've never shown a use of "implicit" to an experienced developer unfamiliar with Scala and seen them get excited about the language. The best case scenario is that they aren't instantly horrified / confused / repulsed.
- harryh 7y agoThere are a lot of tools in the world that are confusing at first but are extremely powerful and useful once you understand their use. implicit is one of them
- dangerlibrary 7y agoI never said it wasn't powerful.
- fiddlerwoaroof 7y agoThe one thing that I like about Scala is Spark which is a really good example of what something like implicits can enable
- sk5t 7y agoAutomatic capture of Manifest / type parameters might be the most immediately appealing thing to a Java person learning about Scala, I dunno. Maybe don't show them implicit conversion.
- Joeri 7y agoI only see a superficial similarity. Case classes are much more powerful. Copy constructor, private constructor, pattern matching, overrideable apply/equals/hashcode. Granted, some of that stuff is esoteric, but it does make case classes a very different beast.
- halfmatthalfcat 7y agoImplicits are very powerful when used tastefully but are being replaced/deprecated in Scala 3 regardless.
- virtualwhys 7y agoActually, implicits aren't going anywhere in Scala 3. There will be new syntax, but otherwise the underlying semantics will remain the same; it's just the implicit keyword itself and the kitchen sink of functionality that it provides in Scala 2 will be split up into new (soft) keywords like "given foo(using: Bar) = ..." rather than "implicit def foo(implicit bar: Bar) = ...", along with "extension listOps of" replacing "implicit class ListOps ...". The community isn't united over the proposed changes (mostly due to migration concerns it seems), but overall it looks like a nice improvement over the status quo, particularly around typeclass definitions.
- ecoffey 7y agore: implicits... https://twitter.com/ecoffey/status/1179198362924404736?s=21 https://twitter.com/ecoffey/status/1179198362924404736?s=21
- LordHeini 7y agoI would say there are tons of other features as well. More often than not Java programming just consists of throwing gang signs on your keyboard for your Ide to create tons of boilerplate. Well case classes can implement traits, might have a companion object and can (poorly) replace enums. Scala sometimes seems to not need standard classes. Other languages like go, rust or c can do just fine without classes at all.
- th0ma5 7y agoThis sounds a lot like Clojure records and it may be some time before I can mentally distinguish them just based on the name alone.
- hyperpallium 7y agoThey're not really "tuples", but classes with auto-boilerplate. e.g. you can't say: (x,y) = getXY(); instead record Coord(int x, int y) {} Coord coord = getXY(); // access as coord.x, coord.y It's not terrible, but it's terribly javay.
- frou_dh 7y agoThat's more lack of destructuring syntax than non-tuple-ness, no?
- hyperpallium 7y agoI think both.
- hyperpallium 7y agoEDIT can you think of a destructuring method that operates with access by name, rather than by position? (lo, hi) = getLim(); It seems to me it would have to be something like: Lim lim = getLim(); lo = lim.minimum; hi = lim.maximum Pwrhaps records could define an ordering of their fields, so as to enable destructuring by position?
- frou_dh 7y ago> can you think of a destructuring method that operates with access by name, rather than by position? Yeah, this in JS: obj = {a: 1, b: 22, c: 333} const {b, a} = obj My original point was more that, even in languages that have both "proper" traditional tuple types and the parenthesised destructuring syntax, we're not obligated to use destructuring. i.e. writing the following is perfectly legitimate, if verbose: a_tup = function_returning_tuple() x = first(a_tup) y = second(a_tup)
- hyperpallium 7y agoI hadn't seen that. It is a bit shorter, because you needn't repeat the source object. My e.g. would be lim = getLim(); const {minimum: lo, maximum: hi} = lim;
- abraxas 7y agoWill arrays of records be contiguous in memory or are we still chasing pointers just as with all other arrays of objects in Java?
- kasperni 7y agoRecords can be combined with inline types when they get there with project Valhalla.
- chris_overseas 7y agoYou'll still be chasing pointers with these. What you (and myself and many others) are looking for are inline types[0][1] that are part of Project Valhalla[2]. [0] https://openjdk.java.net/jeps/169 https://openjdk.java.net/jeps/169 [1] https://dzone.com/articles/valhalla-lw2-progress-inline-types https://dzone.com/articles/valhalla-lw2-progress-inline-type... [2] https://wiki.openjdk.java.net/display/valhalla/Main https://wiki.openjdk.java.net/display/valhalla/Main
- cies 7y ago> Records can be used in a wide variety of situations to model common use cases, such as multiple return [...] I thought multiple return was more interesting in the context of multiple possible types: like a type in case of success and one in case of failure (Either in Haskell, Result in Kotlin and Rust). This type of type is a sum type a.k.a. a tagged union, you could think of as Enum types where each item can have a payload. This comes in really handy and allows Kotlin not to (ab)use exceptions for non-fatal errors. I never had the problem with returning several objects at ones, I would simply write a class that embeds them (and in J14 I can write a record).
- Someone 7y ago”I thought multiple return was more interesting in the context of multiple possible types: like a type in case of success and one in case of failure” That’s or (another) one in case of failure, and not multiple return values. It returns a single value. ”I never had the problem with returning several objects at ones, I would simply write a class that embeds them” One man’s “simply” is another man’s “ugly boilerplate” or “unnecessary production of garbage objects”. You could similarly ”simpler write a class that embeds either of them”, like this: class AorB { private final Boolean itsAnA; private final A a; private final B b; public AorB(A aa) { a = aa; b = null; itsAnA = true; } public AorB(B bee) { a = null; b = bee; itsAnA = false; } ... } Ugly boilerplate? Yes. Not optimally efficient? Yes. Not as nice as the real thing? Yes. Workable? Yes. Good idea? Debatable. Java culture would use an exception if B is an error type.
- cies 7y agoI wonder what the "match" statement would look like on your example. This is exactly as you say though: ugly, inefficient, not-as-nice, yet works. And clearly debatable, especially as it goes against Java culture. The pattern could be used commonly, and clean-up heaps of code, and remove exception's hard-to-wrap-yr-mind-around flow jumps/etc, that I do hope for both tagged unions and match switches on them to be introduced. Some are already demo features IIRC.
- 7y ago
- GiorgioG 7y agoC# has been waiting on record types for a while. https://github.com/dotnet/csharplang/blob/master/proposals/records.md https://github.com/dotnet/csharplang/blob/master/proposals/r... Come on guys!
- llcoolv 7y agoI am quite disappointed - if Java is supposed to compete with Kotlin, they should not just provide an inferior clone of Kotlin features, but push it a step further. Here, it is not the case as those records are basically Kotlin data classes, but not really well thought out. Unfortunately since Oracle took over, Java as a language went in a very dull direction.
- kasperni 7y agoYou do know that the concept of records/data classes goes back long before Kotlin? And what exactly are you missing?
- llcoolv 7y agoWhen it comes to popular languages on the JVM, it doesn't go before Kotlin as Scala was never a popular language to begin with. One thing I and a lot of people are missing is annotations, with them data classes in Kotlin are useful for JPA/REST/AMQP/etc DTOs. Another convenience would be the ability to add at least mapping methods like in Kotlin or implement interfaces (Serializable anyone?) Java records are going to be yet another barely used feature.
- _old_dude_ 7y agoRecord are product types, with union types (sealed types in Java speak) you get algebraic data type, you can take a look to SML to understand why there will be used a lot. Record have an automatic way to propagate annotations from the record component to the generated methods/fields and comes with native support of serialization (like enums).
- llcoolv 7y agoCould you show an example of it? In none of the materials published so far there is anything even remotely similar to what you are suggesting.
- 7y ago
- jfengel 7y agoDoesn't adding a "record" keyword break a lot of existing code? It wasn't a keyword as recently as JDK 8 (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_k...).
- chrisseaton 7y agoCan you give an example of some code it'd break? At what points in a valid legal Java program could you have previously written record, where it would now conflict with this keyword in the locations where it is valid?
- Macha 7y agoUnless the rules of keywords have changed: Map<String, String> record = getSomeData(); This should no longer be valid if record is a keyword?
- chrisseaton 7y agoBut a record keyword isn't valid where you've written it there. So isn't it still an identifier?
- Macha 7y agoNo, keywords are banned as identifiers everywhere, not just in ambiguous contexts.
- chrisseaton 7y agoI don't believe that's true. boolean isRecordStart() { if (token.kind == IDENTIFIER && token.name() == names.record && (peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) || peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) { checkSourceLevel(Feature.RECORDS); return true; } else { return false; } https://github.com/openjdk/amber/blob/4f5927ba8f23c2e40ac467809ef83b7395084355/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java#L4114-L4122 https://github.com/openjdk/amber/blob/4f5927ba8f23c2e40ac467...
- cletus 7y agoSo some immediate thoughts: 1. Why not just call these "structs"? Everyone knows what a struct is. Also making "struct" a reserved keyword is less likely to cause issues than "record"; 2. I get that these are trying to be simple but why can't I compose records from other records? I guess I can have a record member of a record? 3. I couldn't see if records can be passed entirely on the stack like primitive values can. This would be huge. I seem to recall there was some effort to add this to Java but I'm not sure whatever happened to that. It probably added a ton of complexity.
- vmchale 7y ago"record" is used in the Haskell world, for example. Also ATS.
- Polyisoprene 7y agoThey are not that similar to a struct in C in that they are not just about being a data carrier. So that might be confusing if that is what you expect. Future work is intending to solve the third point and add deconstructing the record for pattern matching. “We can declare a simple x-y point abstraction as follows: record Point(int x, int y) { } which will declare a final class called Point, with immutable components for x and y and appropriate accessors, constructors, equals, hashCode, and toString implementations.”
- kasperni 7y ago1. Records are reference types (heap allocated), structs are not. C# have structs and will also be getting records with 9.0 2. Yes, you can make records of other records. 3. Not yet, but Project Valhalla will be bringing inline types to Java https://wiki.openjdk.java.net/display/valhalla/Main https://wiki.openjdk.java.net/display/valhalla/Main
- skywhopper 7y agoWhat do you mean, "structs are not"? As far as I can tell, structs do not exist in Java?
- smabie 7y agoIt’s crazy that Java is still trying to catch up to Scala, yet, Scala seems very maligned (at least on HN, my coworkers love it). This is contraversial, but Java does not provide any value anymore. The JVM on the otherhand, is a remarkable piece of engineering, unrivaled by pretty much anything else in the space. I still can’t get over that Gosling was okay with the first Oak or Java or whatever release. But he’s a super smart guy so I assume there was a reason besides corporate pressure? I’ve seen people on HN disagree, but we, as a profession, really deserve the most powerful tool for the job. Notation as a tool for thought, etc etc. PS: The thesis is that complexity is bad (obviously), but power is good (a priori). Arguments against complexity are fine, but arguments against expressiveness and power are ridiculeous. Moreover, the arguments I’ve heard against power are strikingly similar to those I’ve heard against freedom, or drug legalization, or democracy. They always revolve around how other people are stupid and can’t be trusted with power/freedom/expressiveness/etc.
- vmchale 7y ago> It’s crazy that Java is still trying to catch up to Scala, Or Haskell etc... Plenty of languages have product types.
- vips7L 7y agoIn my experience scalac, scala, and sbt are some of the biggest productivity killers at my work place. Most devs loathe going into the parts of our code base that are scala (thanks Play). The tooling for scala also is not as good as the rest of the Java world.
- Justsignedup 7y agoI worked with Scala fans, and with non-fans. Fans love it because they code in VIM. Non-fans tend to have better tools and thus find Scala's tools very very lacking.
- dtech 7y agoThat is not my experience at all. IntelliJ has excellent Scala support and is very widely used. Quite the opposite, one of the downsides to Scala is that non-IntelliJ editor support is currently lacking.
- vbezhenar 7y agoI have some issues with this feature. 1. I don't really like this immutable approach, I want setters too. 2. I don't need those autogenerated equals/etc implementations, it's just code bloat. May be it's OK to introduce some syntax to automatically generate them like `String toString() default;` but it should not be implicit. 3. It uses non-standard getter syntax. Java developers used `getProp()` forever, now it's `prop()`. Shall we rewrite old classes or tools? Shall we live with two different styles in one codebase? Both approaches are bad. This feature looks very out of place in Java. I hope that it'll be removed in the next release and replaced with something more compatible with old code. I waited forever to get short property syntax in Java and got unusuable feature in the end. I don't want to migrate to Kotlin just because of properties.
- _bxg1 7y ago> I don't really like this immutable approach, I want setters too. > I don't need those autogenerated equals/etc implementations, it's just code bloat. May be it's OK to introduce some syntax to automatically generate them like `String toString() default;` but it should not be implicit. It sounds like you just want... regular classes? I don't understand your position here. Immutable data structures have many uses and are quite popular. If that's not what you want, why use these at all? > Java developers used `getProp()` forever, now it's `prop()`. Shall we rewrite old classes or tools? Shall we live with two different styles in one codebase? C# is the same way. The getProp() convention isn't as strong because it's had computed properties for longer, but one doesn't have to preclude the other.
- Rebelgecko 7y ago>It sounds like you just want... regular classes? I don't understand your position here. Immutable data structures have many uses and are quite popular. If that's not what you want, why use these at all? Records provide a lot of syntactic sugar that classes don't (which is the whole reason to use them— there's nothing stopping you from creating a regular class with all final fields). edit: I'm assuming that the "finality" of fields in records is identical to the shallow finality of a final field in an object
- 7y ago
- dwohnitmok 7y agoRecords are great. If sealed classes become a thing (https://openjdk.java.net/jeps/360 https://openjdk.java.net/jeps/360) I'll be absolutely elated. And it would be just the cherry on top if the two could be used together (along with a private constructor). Scala has this, i.e. `sealed abstract case class`, Kotlin does not, i.e. `sealed data class` is not valid. I am thrilled that algebraic datatypes (effectively the combination of records and sealed classes) are finally entering mainstream consciousness and that statically typed languages all over the place are starting to adopt them.
- jillesvangurp 7y agoKotlin data classes cannot be extended and they can only extend sealed classes. So they are effectively sealed already.
- dwohnitmok 7y agoI perhaps should have been more explicit about the goal rather than the method, since sealing records is only one part of this. This is all in service of private constructors. You effectively cannot have private constructors for data classes in Kotlin because the copy method remains public and cannot be "turned off." `sealed abstract case class` in Scala gets rid of that. Private constructors for record types is a fantastic light-weight way of incorporating runtime checks into compile-time types.
- pron 7y agoThat's the plan: https://cr.openjdk.java.net/~briangoetz/amber/datum.html https://cr.openjdk.java.net/~briangoetz/amber/datum.html
- fctorial 7y agoAt first I thought it was project valhalla merging into mainline java. Apparently this is a java language feature. Java is starting to move away from being the C of jvm. Java constructs are no longer direct mappings of jvm constructs.
- tristanstcyr 7y agoI think that this ship has sailed quite some time ago. For example, inner classes which were introduced in Java 4 (if my memory serves right).
- lokedhs 7y agoI'm pretty sure it was in Java 1.2. That's when Swing was implemented, and I don't recall ever having used Swing without inner classes.
- AzzieElbab 7y agoCool. Can't wait until java people start going crazy about profunctor optics
- augusto-moura 7y agoGuess everyone can dream
- gcb0 7y agoRecords is the most useful change and it's been bumped from 9, to 10, to 13, now to 14... sigh. at least we got some five new syntax for Switch in between!
- chaostheory 7y agoDid I miss something or will this feature make it into more license friendly versions of Java? Otherwise what's the point if you have to pay Oracle extortion money?