JEP 502 - Stable Values. New Feature of Java 25 Explained

Fri, 29 Aug 2025 14:13:00 +0000
Java, uncoverjava, java 25, JEP502

In this article, we will look at JEP 502 - Stable Values. It’s a new feature that will appear with Java 25, our new LTS, planned to be released in September. It is going to be introduced as a first preview feature, which means that everything can be changed.

What is Stable Value?

A StableValue <T> is a container that holds a single value of type T. Once assigned, that value becomes immutable. You can think of it as an eventually final value.

It’s vital to notice that a reference to an object will be immutable. The object beneath can be changed.

Before Stable Values

Before Java 25, to achieve immutable data, we had to use the final keyword.

class Controller {
 private final EmailSender sender = new EmailSender();
}

There were some problems with this approach.

Whenever our code has a final field, it has to be set eagerly, either initialized by a constructor or by having a static field. Because of that, the application's startup may suffer. Not all fields are immediately needed, right?

So maybe we could remove the final keyword and do something like that:

class PetClinicController {

 private EmailSender sender = null;

 EmailSender getSender() {
   if (sender == null) {
     sender = new EmailSender();
   }
   return sender;
 }

 void adoptPet(User user, Pet pet) {
   // some logic here
   getSender().sendEmailTo(user, "You are great person!");
 }
}

It’s going to work, right? The startup will be faster. And yes, that's one way to solve this issue before Java 25. Unfortunately, it's not free.

Our sender is still mutable; we can assign a different value to this variable. However, we would need to use external tools to prevent us from doing that, or be very precise during code reviews.

Potential null pointer exception while accessing a field from another way than just using a getter.

It would be good to have "getSender" thread-safe.

But even when we do so, we prevent the JVM from optimizing access for this field. For example, using constant-folding.

Let's use some Stable Values

Using our example from before, let's migrate to the new Java.

class PetClinicController {

 private final StableValue<EmailSender> sender = StableValue.of();

 EmailSender getSender() {
   return sender.orElseSet(() -> new EmailSender());
 }

 void adoptPet(User user, Pet pet) {
   // some logic here
   getSender().sendEmailTo(user, "You are great person!");
 }
}

The code is very similar, but StableValue handles the nullability of EmailSender. Thanks to that, it’s impossible to call EmailSender without first calling a method to retrieve the value.

The value inside a StableValue is guaranteed to be set in a thread-safe way.

Stable functions

Stable Values provide the foundation for higher-level functional abstractions. Right now, we have three types of stable functions.

Supplier:

It is a function that will be computed only once, and the result of this will be stored and returned. For example, it can be used in our Controller.

class PetClinicController {

 private Supplier<EmailSender> sender = StableValue.supplier(()-> new EmailSender());

 void adoptPet(User user, Pet pet)...