Core Java

Local records or classes to improve the readability of stream operations

Java 14 came with the preview language feature of Records — a special lightweight class, comparable to similar constructs in other languages, such as record classes in C#, data classes in Kotlin and case classes in Scala.

There’s A) already numerous blog posts explaining Java 14 records and B) numerous articles comparing usage of records with Project Lombok’s @Value (for making immutable classes), so I won’t do that again here. 😉

Brian Goetz explains in JEP 384: Records (Second Preview the motivation behind them and the rules, such as restrictions on the declaration of a record, and the similarities with a “normal” class.

My eye caught the section of local records:

A program that produces and consumes records is likely to deal with many intermediate values that are themselves simple groups of variables. It will often be convenient to declare records to model those intermediate values. One option is to declare “helper” records that are static and nested, much as many programs declare helper classes today. A more convenient option would be to declare a record inside a method, close to the code which manipulates the variables. Accordingly, this JEP proposes local records, akin to the traditional construct of local classes.

In the following example, the aggregation of a merchant and a monthly sales figure is modeled with a local record, MerchantSales. Using this record improves the readability of the stream operations which follow:

Photo by Dominika Roseclay.

The MerchantSales below is a mutable tuple, holding both a single Merchant and the sales which is computed as the stream is processed. We need to capture both, to be able to sort on the computed sales but ultimately return the (original) merchant for that sales.

01
02
03
04
05
06
07
08
09
10
11
List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
 
   // Local record
   record MerchantSales(Merchant merchant, double sales) {}
 
   return merchants.stream()
       .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
       .sorted((m1, m2) -> Double.compare(m2.getSales(), m1.getSales()))
       .map(MerchantSales::getMerchant)
       .collect(toList());
}

The fact that this is a record defined in a method makes this a local record, and I could immediately recognize the advantages in many Stream API situations where the stream needed to accumulate many values grouped together: like the example shows, map X, calculate or generate Y and keep both around for the next steps in the stream.

Of course, in all these situations I worked around it by obviously also introducing a helper POJO, or re-designing the entire stream logic, but reading the JEP made me remember that Java supports local classes (not records; I mean actually plain classes) pretty much since the beginning.

Local classes are non-static because they have access to instance members of the enclosing block.

Local records and local (inner) classes increase the use of encapsulation. You don’t need to make the type more widely available outside the block where it’s created.

Here’s how the example looks like with a local class. I’m using Lombok’s @Data which generates the required argument constructor and getters/setters to stay in the spirit of less-verbosity-is-more, but you can always use plain vanilla Java too.

01
02
03
04
05
06
07
08
09
10
11
12
13
List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
   // Local class
   @Data class MerchantSales {
      final Merchant merchant;
      final double sales;
   }
 
   return merchants.stream()
       .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
       .sorted((m1, m2) -> Double.compare(m2.getSales(), m1.getSales()))
       .map(MerchantSales::getMerchant)
       .collect(toList());
}

So, when not on Java 14 yet, or can’t enable the records preview feature, one can always use a local class instead to improve the readability of stream operations.

51.825217
5.812153

Nijmegen, Nederland

Published on Java Code Geeks with permission by Ted Vinke, partner at our JCG program. See the original article here: Local records or classes to improve the readability of stream operations

Opinions expressed by Java Code Geeks contributors are their own.

Ted Vinke

Ted is a Java software engineer with a passion for Web development and JVM languages and works for First8, a Java Web development company in the Netherlands.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
MarkUltra
MarkUltra
4 years ago

Pretty interesting to watch for the increasing influence of the IT-sphere. Just 15 years before it was much less popular. IT specialists are demanded in almost every successful company. But sometimes it is better to use outsourcing specialists when you have some individual issues. Hiring freelance salesforce consultants is a great idea, as I think. https://www.ergonized.com/blog/how-to-hire-freelance-salesforce-developers-consultants-and-administrators-in-2020/ just check the link to find out more information.

Bill Fly
Bill Fly
4 years ago

An elaboration on the computeSales method would be helpful. 
Also, unlike Lombok, Java records use accessor methods named the same as the attribute.

Ted Vinke
4 years ago
Reply to  Bill Fly

Hi Bill, thanks for mentioning the accessor methods – yes, it was a copy-paste mistake on my part. Fixed it in the original article, but don’t know if JCG is going to detect this ;-)
Thanks!

Ann Rich
Ann Rich
3 years ago

You helped me so much as a writer’s conference newbie with the same advice you just wrote. And when I was a college journalism major, our prof told us the same for newspaper writing. Thanks!

Sharon
Sharon
3 years ago
Veronika
Veronika
1 year ago

thank you!

Back to top button