7 ms·
I feel like Java is making great strides lately, but that we're also getting slightly lesser versions of capabilities available in other languages. Records, fo
by thomascgalvin 7y ago
I 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 agoScala lets you quietly replace a field with an accessor method, but Java does not. You'd have to change all consumer code to add laziness or test mocking/spying.
- doctor_eval 7y agoin my experience accessor methods are a particularly nasty kind of YAGNI. I'm not saying you never need to override a field to make it lazy, for example, but the cost of getters and setters has never, in my practical experience, outweighed the cost.
- erik_seaberg 7y agoIf they're generated and inlined away, what's the remaining cost?
- doctor_eval 7y agoThe cost is in typing effort, fluency, legibility and complexity. There is no Good reason to prefix every field access with get, and end it with (). It’s five more keys to type and makes the code more verbose. thing.getId() is longer and less readable than “thing.id”. Why does the compiler have to spend time inlining the call? Can you guarantee that it does? IMO the question shouldn’t be “what is the cost of this complexity?” The question should be, “what is the benefit?”. And in 20+ years of java dev, I just don’t see any benefit in accessor methods at all. We never wire up our components with JavaBeans introspection... so why do we keep this nonsense going?
- pron 7y agoYou're confused because you don't understand what this feature is and compare it to things with different goals. https://openjdk.java.net/jeps/359 https://openjdk.java.net/jeps/359: > Records provide a compact syntax for declaring classes which are transparent holders for shallowly immutable data... While it is superficially tempting to treat records as primarily being about boilerplate reduction, we instead choose a more semantic goal: modeling data as data. (If the semantics are right, the boilerplate will take care of itself.) It should be easy, clear, and concise to declare shallowly-immutable, well-behaved nominal data aggregates... It is not a goal to declare "war on boilerplate"; in particular, it is not a goal to address the problems of mutable classes using the JavaBean naming conventions.
- thu2111 7y agoI think the point a lot of posters are getting at is, maybe reducing boilerplate for JavaBeans is a more useful goal, given the huge ecosystem of tools and code that assumes the JavaBean pattern. That's what Kotlin did and it's been very successful, so there's no fundamental reason why not. It's a philosophical objection of the form "mutability is bad", which feels somehow Haskellish and un-Java-like. What happened to being a blue collar language?
- pron 7y ago> so there's no fundamental reason why not That another language has done it does not mean there is no fundamental reason not to do it. A fundamental reason can be that its cost/benefit ratio isn't good enough. Other languages weigh language complexity less harshly than Java. > That's what Kotlin did and it's been very successful In what sense has it been "very successful"? That the small minority of people on the Java platform that have chosen to use Kotlin like this feature? Currently, there are still more people using Scala, does that mean that all Scala features have been even more successful? In any event, that would be a different feature, and it will be judged, and then perhaps adopted, separately. > It's a philosophical objection of the form "mutability is bad", which feels somehow Haskellish and un-Java-like. What happened to being a blue collar language? That's not the objection, and Java is still blue-collar. Records don't do that not because of some "anti mutation" sentiment but because records are about something else than reducing JavaBean boilerplate. See the discussion on mutability here, https://cr.openjdk.java.net/~briangoetz/amber/datum.html https://cr.openjdk.java.net/~briangoetz/amber/datum.html, which I'll reproduce below for your convenience: > The stricture against mutability is more complex, because in theory one can imagine examples which do not fall afoul of the goals. However, mutability puts pressure on the alignment between the state and the API. For example, it is generally incorrect to base the semantics of equals() and hashCode() on mutable state; doing so creates risks that such elements could silently disappear from a HashSet or HashMap. So adding mutability to records also likely means we may want a different equality protocol from our state description; we may also want a different construction protocol (many domain objects are created with no-arg constructors and have their state modified with setters, or with a constructor that takes only the "primary key" fields.) Now, we've lost sight of the key distinguishing feature: that we can derive the key API elements from a single state description. (And, once we introduce mutability, we need to think about thread-safety, which is going to be difficult to reconcile with the goals of records.) > As much as it would be nice to automate away the boilerplate of mutable JavaBeans, one need only look at the many such attempts to do so (Lombok, Immutables, Joda Beans, etc), and look at how many "knobs" they have acquired over the years, to realize that an approach that is focused exclusively on boilerplate reduction for arbitrary code is guaranteed to merely create a new kind of boilerplate. These classes simply have too many degrees of freedom to be captured by a single simple description. > Nominal tuples with clearly defined semantics is something that can make our programs both more concise and more reliable for a lot of use cases -- but there are still use cases beyond the limits of what they can do for us. (That doesn't mean that there aren't things we can do for these classes too -- it just means that this feature will not be the delivery vehicle for them.) So, to be clear: records are not intended to replace JavaBeans, or other mutable aggregates -- and that's OK.
- krzyk 7y agoBean compliant getters are horrendous, most newer libraries avoid them.
- pron 7y agoRecords are actually inspired by product types, and will be central to the pattern-matching feature that has started making its way into the language (and, combined with the upcoming sealed types, would form Java's ADTs). Allowing mutation doesn't make much sense, then.
- mahkoh 7y ago>Allowing mutation doesn't make much sense, then. Why?
- pron 7y agoBecause that's not what records are about. See https://openjdk.java.net/jeps/359 https://openjdk.java.net/jeps/359, https://cr.openjdk.java.net/~briangoetz/amber/datum.html https://cr.openjdk.java.net/~briangoetz/amber/datum.html and https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.... For one, mutation doesn't play well with pattern matching.
- mahkoh 7y agoExplain why mutation doesn't play well with pattern matching.
- pron 7y agoA pattern requires matching against a piece of data possibly composed of several related components that are assumed to be coherent with one another, not some mutable object. How would you match Point(3, y) against a mutable object, if by the time you grab the y, the x component may no longer be 3 (recall that Java is a multithreaded language, and x could be volatile)? By analogy, think of how matching a regex pattern would work against a mutable array of characters as opposed to an immutable string.
- mahkoh 7y agoDifferent components being in sync is a property of objects in general. E.g. Java's ConcurrentHashMap is mutable but its fields are in sync. For pattern matching to see an in-sync view of the object, the object must provide such a view. If the object allows concurrent modification (most don't) then it must provide a custom view. Otherwise a generated default view should be sufficient. If you look at [1] you can see that Goetz does propose a function that provides such a view for classes and not just records (c-f extractor.) [1] https://cr.openjdk.java.net/~briangoetz/amber/pattern-semantics.html https://cr.openjdk.java.net/~briangoetz/amber/pattern-semant...
- virtualwhys 7y ago> 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. Sounds a lot like Scala case classes. More accurate to say that both Java records and Kotlin data classes are inspired by Scala case classes -- not that it matters, programming languages authors are constantly stealing ideas (sorry, drawing inspiration) from other languages; thereby moving the state of the art into the mainstream.