Java Records: Making Bad Designs More Convenient
While "records" seem to be universally welcome, developers, especially fans of the object-oriented paradigm, should be careful when considering this new feature.
Join the DZone community and get the full member experience.
Join For Free
There is an official JEP 359: Records (preview) that proposes to introduce "Records", i.e. pure data structures into Java. With it, Java continues to follow other contemporary languages into the territory of "multi-paradigm" programming.
This article explores what this change means for object-orientation, Java development, and our industry in general.
Old Name, Old Feature
First of all, "records" are not new nor modern — quite the opposite actually. Records and structures have been around forever in programming; they were present in COBOL in the 60s, and then later in widely used languages such as Pascal, C, and others.
These languages followed the procedural programming paradigm, which meant that data was modeled as plain data, and then there were procedures where the "business-logic" was implemented by manipulating the data that was either passed as a parameter, available globally or from somewhere else.
Then object-orientation and Smalltalk came along in the 70s and 80s and did away with pure data structures in favor of objects, which were thought of as independent agents cooperating and communicating with messages to implement the requested logic. Even in the 90s when Java was created, this original idea still persisted strongly enough that Java originally did not introduce pure data structures.
Then, our industry virtually exploded in the late 90s; "e-business" was now a thing. Java introduced the "Enterprise Edition," and there was a huge demand for Java/JavaEE developers. This is where cracks started to appear on the transition to object-orientation. There were not enough object-oriented developers, so most people (including myself admittedly) came from a procedural background. At the team where I was a junior developer, nobody explained how object-orientation was supposed to work, so we basically continued to produce C code in Java. Instead of data structures, we used "Beans," and instead of procedures, we used methods that were awkwardly grouped into random classes, usually called Managers. Most projects at the time operated this way.
In the 20 years since then, this basic design did not change much. Layered architectures were already a thing in the 90s, still a thing today. Having a data model (sometimes called "domain model" these days) separately from the logic is still the dominant approach for Java software development by far.
So with this in mind, it seems pretty clear that introducing records is not a step forward at all, rather it is literally a step back towards our procedural past. Were things in the past better, was the procedural approach successful?
The State of Our Industry
To complete our historical perspective, it might be beneficial to look at where we are now in more detail. I will not be able to summarize that more succinctly than this.
Just to stay in the "enterprise" sphere, there is an adversarial relationship between the business and development. Not between people mind you, but between the areas. Development is usually not part of the business at all, they are a "service" the business reluctantly keeps around or even outsources. Development and IT, in general, is a "cost center". That is MBA talk for a thing that just costs money but is not considered useful.
This wasn't always like this. Even as late as the 90s, business people were excited to work with us, to tell us how they operated, and asked us to help them come up with new ideas for customers. We worked side by side with them not just developing software, but as a part of business operations. People trusted us because we were there the whole time, saw what was happening with them, and how they worked. We came up with small enhancements almost weekly. We were also not budgeted separately, but with the business group, we were working in. We helped generate money so to speak, instead of just burning it.
So what changed? Well, many things, but most importantly, we were not good enough at our jobs. Things took too long to build, they were not working as expected, and as codebases grew, things got only worse. Then the business started pushing us away and demanded to have some form of contractual security, which resulted in less trust and communication, and so the spiral continued downward.
Despite these changes, we, as an industry, continued to do things the same way. We still think the same way and we still mostly use the same, essentially procedural approach for software design with some minor cosmetic changes. So even though Java did try to make the step forward towards object-orientation, it seems it now acknowledges that it was largely unsuccessful and it officially reverts back to the more popular procedural approach, that from a historical perspective, we already know doesn't really work well. So why would Java do this?
Motivation and Goals
The main reason given in the proposal to have records is for "modeling data as data," which would look like this:
record Point(int x, int y) {
}
Let's look at this approach pragmatically. How readable and maintainable will this code become? Let's ask some questions:
- What does the application do with it?
- Can I change the
int
tolong
? - Can I change the Cartesian coordinate system to polar coordinates?
- Would polar coordinates be more optimal representation?
- If I would extend this
Point
to 3D with az
coordinate, what would I have to change?
These are just some of the things we might be interested in when we look at Point
. If you use such a construct in an application, you'll have to search the code for usages, often following through multiple layers and even through other objects where the data is simply copied out to be used somewhere completely unrelated.
So instead of that, how about this:
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Line connect(Point other) {
...
}
public Line throughAt(double angle) {
...
}
public Circle circle(int radius) {
...
}
}
This is obviously more code, but also much more information. We know what the Point
is for and how it is used. We see that the internal representation stays internal, so there is no way the application uses the Point
in some other way not defined here. We can also change this internal representation freely, look at whether polar coordinates would be better in light of the logic present here, etc. And we don't have to look elsewhere to figure all this out.
So which is more "readable"? Which communicates more information? Which is more maintainable? Which is guaranteed to be used correctly? Which can be abused less?
The reality is of course that the proposed "records" are only more readable compared to conventional data-only "objects" in Java, but the proposal completely ignores that the approach itself could be wrong from a historical, maintainability, readability and design perspective.
Functional Programming
There are some arguments suggesting that records are not just read-only "structs" from C, but actually "algebraic data types" from functional programming. And because the words "algebraic" and "functional" sound cool, it must be good.
Functional programming, at its core, is the idea that software should be a composition of pure mathematical functions. Functions that receive some values (i.e. immutable data) and produce always the same value for the same parameters. Java doesn't even have a concept of immutable data nor pure functions, nor proper tools for function composition. Even the proposed records are only shallowly immutable, meaning they can refer to other objects that are mutable.
Even ignoring all that, actual real-life designs in Java are clearly not functional, but mostly procedural. It is therefore pretty clear that records will be used to support current designs, regardless what its purpose was supposed to be.
You Don't Have to Use It
This argument has to be addressed because this is a frequent response to critique on a new feature: Well, you don't have to use it if you don't want to.
This, of course, completely ignores that none of us work in isolation. We work with colleagues, external partners, consultants, or even people we have never even met. Anything the language supports will be used and everybody will have to deal with it.
Also, features of a language suggest usage, an idiomatic way certain solutions are designed and used. To suggest that a language feature can be ignored is, therefore, unrealistic.
Summary
The coming Java "record" feature is not new at all; it is, in fact, an official regression into procedural programming. That is the paradigm from the 60s that is partially responsible for the "fall from grace" of software development in the 90s and 2000s. It is a signal that the transition to object-orientation, the paradigm that had the potential to fix the issues of the procedural world, that Java began in the 90s is basically abandoned.
The notion that records are part of the functional paradigm is a red herring. Java is not, and never was, functional, and real-life Java code is pretty much procedural if anything.
So, while "records" seem to be universally welcomed, people, especially developers committed to the object-oriented paradigm, should be extra careful when considering this new feature, or even refrain from using it completely.
Further Reading
Published at DZone with permission of Robert Brautigam. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments