It's not hard to write really convoluted, unexpressive code and in terms of instructions-per-LOC, other (JVM) languages will run circles around Java. Newer versions of the JDK however, definitely added a lot of improvements. It's not realistic to keep up with each and every change, but it's beneficial to at least have a rough idea of what kind of features exist.

For example, recently I came across a piece of code which was interacting with a Map.  A simple 2D data structure, mapping some enum to another map of dates corresponding to a value. The context doesn't really matter but for brevity let's assume it's a table of shirt colors, with a revenue amount per day.

Map<Color, Map<LocalDate, BigDecimal>> mapWithData;

The map is filled and mutated in a loop and using a stream wouldn't make much sense. The code for aggregation looked something like this:

// Color color; LocalDate date; BigDecimal amount;
if (mapWithData.containsKey(color)) {
    if (mapWithData.get(color).containsKey(date)) {
        // color & date exists, add to current value
        mapWithData.get(color).put(
            date, 
            mapWithData.get(color).get(date).add(amount)
        );
    } else {
        // color exists, date not yet
        mapWithData.get(color).put(date, amount);
    }
} else {
    // color does not yet exist, create anew and add date + amount
    Map<LocalDate, BigDecimal> newData = new HashMap<>();
    newData.put(date, amount);
    mapWithData.put(color, newData);
}

This code is functionally correct and has a set of unit tests proving that. It's not hard to grasp and if you would ask a developer which has zero knowledge of Java, they would probably be able to create a mental model of what this code does: when the map doesn't have an index for color it creates it anew and starts with the amount for the current date. If the color has been seen before, it either adds the amount to the current value for date or inserts it as a new value.

Opportunistic refactoring (or: be a boy scout)

This specific part of code wasn't wasn't the target of any intended changes. I'm however a big fan of opportunistic refactoring. You might notice the unnecessary multiple mapWithData.get() operations and then get rid of the redundant containsKey check. But, when using standard APIs it can be quickly rewritten as follows:

// start with finding the date-to-amount map, create a new map if it doesn't exist
mapWithData.putIfAbsent(color, new HashMap<>())
        // compute the value for the date key by either starting with a new amount or adding to it
        .compute(date, (key, oldAmount) -> oldAmount == null 
                                           ? amount 
                                           : oldAmount.add(amount));

Functionally, this code is equivalent to the earlier snippet. It just uses a more functional approach and relies on lambdas. These API's are not that new at all, they exist since Java 8 and the earlier snippet was written in a Java 11 codebase! Asking the developer who originally wrote it, they simply acknowledged they didn't know these APIs existed.

If you haven't checked the Map API since a (very) long time, you might still think it only has basic get and put operations. Java rightfully so has a bad rep for being terse and overly verbose but if there's standard API's available to make it a bit more expressive, why not learn a bit about them and take advantage of it?

Just an example

This specific change is probably not the only or best way to do it; it's just to illustrate that sometimes there might be a quicker or more elegant way to tackle a problem. Another example might be the improvements regarding pattern matching. Previously you would write:

if (someObject instanceof BigDecimal) {
    BigDecimal myBigDecimal = (BigDecimal) someObject;
    myBigDecimal.add(ONE);
}

Since Java 14 you can cast directly via an instanceof check:

if (someObject instanceof BigDecimal myBigDecimal) {
    myBigDecimal.add(ONE);
}

If you don't have the vaguest idea of which language features are being worked on or added in new releases, perhaps you will only find out about this if you happen to come across it. (Or use a linter! For example Sonar has a rule to spot this.)

Conclusion

I'm definitely not advocating for rigorously memorizing each and every language feature and API but it wouldn't hurt to scroll through the Java 21 release notes for example. There's also a myriad of blogs and resources (some of better quality than others) dissecting new releases and features. Even if you are still stuck working on a Java 8 codebase, you will hopefully upgrade some day and then at least you know what kind of new features you can use.

Want more content like this?

Hi! I sporadically write about tech & business. If you want an email when something new comes out, feel free to subscribe. It's free and I hate spam as much as you do.

your@email.com Subscribe

On not knowing your Java API's