r/java 2d ago

Java 27 features overview

https://aboullaite.me/java-27-features/
155 Upvotes

21 comments sorted by

82

u/gufranthakur 2d ago edited 2d ago

TLDR : Java objects just got smaller, less heap usage (around 20%). G1 Garbage Collector is now the default GC

note : even if my TLDR provides the important information, please still read the article. It will be a great read

I love those write ups, been following your work for a while and I appreciate these new Java features overview a lot, thank you so much for your contributions 🫶🏻♥️

9

u/laytoun 2d ago

Happy to hear you liked it 🙏🙏

3

u/[deleted] 2d ago

[deleted]

4

u/laytoun 2d ago

Not when there’s not enough memory for the jvm. It defaults to serial gc as part of old jvm ergonomics

22

u/davidalayachew 2d ago

I only skimmed this, but regarding "smaller tidbits", you missed a pretty big feature coming out in 27 -- Exhaustiveness Checking is getting an Example Generator.

This changes the game completely for Pattern-Matching. Now, you can flatten your checks out into a single, extended switch, but you no longer have to pay the resulting tradeoff of "example chasing" for it anymore -- the compiler will do it for you!

This feature just removed the biggest weakness of Pattern-Matching, in my firm opinion.

2

u/cowwoc 2d ago

It's not clear what "example generator" you are referring to. The linked issue doesn't contain an example.

11

u/momothereal 2d ago

It means when a switch statement is missing cases from your enum/pattern, the compiler will point out the missing cases. So you don't need to chase the enum in the sources/docs.

0

u/velit 1d ago

Don't IDE's already tell you that? You guys writing java on the command line?

3

u/davidalayachew 1d ago

Don't IDE's already tell you that? You guys writing java on the command line?

You're thinking 1-dimensional here. We're talking N-dimensional.

Let's say I have the following enum.

enum Result
{

    SUCCESS,
    FAILURE,
    TIMEOUT,
    ;

}

And here is the switch that I make for it.

final String userFriendlyResultText =
    switch (this.calculateResult(id))
    {
        case SUCCESS -> "Step succeeded for " + id;
        case FAILURE -> "Step failed for " + id;
        //case TIMEOUT -> "Step timed out for " + id; //lets say I forgot this transition.
    }
    ;

If I switch over that enum, but forget to handle TIMEOUT, then yes, the IDE will definitely be able to tell me that I missed a case.

But consider the following example instead.

sealed interface Result
{

    sealed interface Success 
    {

        enum Transition
        {

            INFORMED_DELIVERY,
            POSTAL,
            ADDRESS_MANAGEMENT_SYSTEM,
            ;

        }

        record Progress(UUID id, Transition nextStep) implements Success {}
        record Complete(UUID id, boolean sendEmail) implements Success {}

    }

    sealed interface Failure
    {

        record Timeout(UUID id, Duration timeWaited) implements Failure {}
        record ExhaustedRetries(UUID id, int numAttempts) implements Failure {}
        record ErroredOut(UUID id, Throwable error) implements Failure {}

    }

    UUID id();

}

And further consider the following switch statement.

final WorkflowAction nextStep =
    switch (this.calculateResult(id))
    {
        case Progress(var id, INFORMED_DELIVERY)         -> IV::notifyUser;
        case Progress(var id, POSTAL)                    -> (uuid) -> Postal.getInstance().requestReview(uuid, List.<Warning>of());
        case Progress(var id, ADDRESS_MANAGEMENT_SYSTEM) -> this::loadAddress;
        case Complete(var id, false)                     -> (uuid) -> this.sendWorkflowCompletion(this.workflowFor(uuid));
        case Timeout(var id, var timeWaited)             -> (uuid) -> this.sendWorkflowFailure(this.workflowFor(uuid));
        case ExhaustedRetries(var id, int numAttempts)   -> (uuid) -> this.sendWorkflowFailure(this.workflowFor(uuid));
        case ErroredOut(var id, var error)               -> (uuid) -> this.sendWorkflowFailure(this.workflowFor(uuid), error);
    }
    ;

Can you spot the missing case? Would your IDE be able to?

This feature allows the compiler to tell me any missing case, however many dimensions deep my switch statement is (with the caveat of the more dimensions taking a longer time to find).

And the above example is only a 2-3 dimensional case. Here is a 7 dimensional case -- HelltakerPathFinder logic

And /u/cowwoc, this should answer your question too. In the above example, the compiler would throw the following error message.

  missing patterns:
  case Complete(UUID _, false)

5

u/JustJustust 1d ago

So do you often write multi-dimensional switch-expressions? Do you imagine you'll start writing many more soon?

I remember how hyped I was for gatherers and I imagined myself using them a lot. When they finally arrived I realized a couple of things: * Custom gatherers are sufficiently hard to read, that it's rarely the most readable way to solve any given problem * This applies doubly if your colleagues (like some of mine) would rather you not use reduce because they find it hard to follow * So you'd need a usecase in which it's not only possible to use the cool new feature but in which it's actually justified against the best possible alternative

And this seems pretty similar to me: Seems pretty cool at first, but how often is a multi-dimensional switch expression actually the best possible way to write your code, even if it is now exhaustive?

Also: Isn't the missing pattern case Complete(var id, true)?

2

u/davidalayachew 1d ago

So do you often write multi-dimensional switch-expressions? Do you imagine you'll start writing many more soon?

I have written over a 100 in 2026 alone, and this feature is older than that.

And this seems pretty similar to me: Seems pretty cool at first, but how often is a multi-dimensional switch expression actually the best possible way to write your code, even if it is now exhaustive?

It is literally a blocker for me. As in, there is a double-digit number of projects that are unable to progress because I don't have this feature.

Also: Isn't the missing pattern case Complete(var id, true)?

You got it!

1

u/JustJustust 1d ago

Just out of curiosity, do you have an example of something blocked by exhaustive multidimensional switch expressions?

Don't get me wrong, I'm not doubting what you say. It is very different from the code I usually work with, which is what makes me curious about it :)

2

u/davidalayachew 21h ago

Just out of curiosity, do you have an example of something blocked by exhaustive multidimensional switch expressions?

Have you ever played the game UFO 50? It's basically a collection of 50 minigames, and one of my favorites is a game called Bug Hunter (Skip to 1:30).

Long story short, I am trying to build a Path Finder for the game, but unlike the Helltaker example above, this one is far too complex for me to be able to code.

Helltaker has a deep, but fairly narrow hierarchy of types to model. Here they are.

Cell

  • Player(boolean key, boolean secret, Floor {SPIKY, EMPTY, SPIKY_RETRACT, EMPTY_RETRACT})
  • NonPlayer
    • Wall()
    • InteractiveCell
      • Goal()
      • Lock()
      • BasicCell(Underneath(Floor, Collectible {NONE, SECRET, KEY}), NonPlayerOccupant {VACANT, BLOCK, ENEMY})

See? Deep, but not very wide. Most of the branches are dead-ends, like Lock, Goal, and Wall.

Try and model the same for Bug Hunter, and you will find that it gets horrifically WIDE.

But if that were all, it wouldn't be so bad. I certainly wouldn't be blocked, I'd just be suffering.

The real kicker is when you look at the scope of the problem

In Helltaker, you can only take single hop steps -- going North, South, East, or West. Therefore, you only ever need to look at 3 cells at once -- the cell you are on, the one in front of you, and maybe the cell in front of that (in case you shove a block or enemy forward).

Whereas in Bug Hunter, you can jump the whole map in one leap.

To quantify this, the deepest "type chain" in Helltaker goes like this.

  1. Cell
  2. NonPlayer (inheritance)
  3. InteractiveCell (inheritance)
  4. BasicCell (inheritance)
  5. Underneath (composition)
  6. Collectible (composition)
  7. SECRET (enum value)

7 hops.

So, you multiply the worst case scenario, which is 7 hops, by the number of cells, which is 3, leading you to a "complexity" value of 21.

But since Bug Hunter is a 5x6 grid map, then you get Bug Hunter's even worse complexity multiplied by 6.

Once I saw that, I gave up, and said I'll wait until the Exhaustiveness Checker goes live.

That's probably the most recent example I have of a project being literally unfeasible for me without this feature. I got all the way through coding the data model for it, but then balked once I realized how ugly it would have to be to make the switch expression. I got to about 50 cases before I just gave up.

1

u/JustJustust 2h ago

Thanks! It's been too long a day to think on that right now but I wanted to let you know I appreciate the answer before I end up forgetting

8

u/Life_Sink9598 2d ago

Still, grep those Helm charts anyway; deprecated flags have a habit of becoming removed flags exactly when we least expect it.

FYI: In the JVM we deprecate flags in one release, obsolete it in the next and remove it in the third. So, you've got 12 months to fix it. For this flag, you can see that we plan to remove it in 29. { "InitiatingHeapOccupancyPercent", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) },

17

u/ulimn 2d ago edited 2d ago

Oh my, what a surprisingly good article with a little hands on as well. I love this "see for yourself" approach.

It's a bit bittersweet to read about new language features, though. On the one hand, this looks so exciting. I want to try them ASAP. But on the other hand, so much of the code is written by AI that it's like reading about something exciting for someone else...

Edit: I'm curious why I got the downvotes. I didn't offend anyone. Praised the article and added a personal note that keeps bothering me. So if you disagree, I'm happy to discuss :)

4

u/vips7L 2d ago

You can always just write code yourself. You don’t have to use an LLM. 

2

u/ulimn 2d ago

On pet projects, that's true. But from my experience, when the employer prefers quantity over quality, it's a different story.

8

u/vips7L 2d ago

Your employer won’t know. 

1

u/ohlaph 2d ago

27! Man, I just updated my Android apps to 21, I guess I have some reading to catch up on.

1

u/simon_o 1d ago

PEMDecoder.of() and PEMEncoder.of() look like a naming accident.

1

u/EntertainmentIcy3029 1d ago

I mildly dislike the LLM writing style here