Null-Restricted and Nullable Types
Jeremy Lyon
jeremy.lyon at fastmail.net
Thu Nov 13 10:11:19 UTC 2025
_RE: Null-Restricted and Nullable Types_
I have been anticipating Valhalla in my work for a while. In doing so I have implemented interfaces that label value objects and nullability. In my code I have 10s of types defined using label interfaces to be both value types and non-nullable types. The idea was that I would anticipate Valhalla and convert the types when Valhalla arrived. I am pleased to say that with the current Valhalla values I am able to convert the relevant types to value classes.
I am concerned about nullability, hence this e-mail. Indicating nullability using ! and ? looks like making a big mistake to correct 'the' big mistake of nulls, at least to my eyes.
Rather than introduce markers to handle nulls, which seems likely to have the effect of baking-in null handling everywhere, I think it better to eliminate nulls altogether and obviate the need to litter the code with null-handling mechanisms. Better to make the problem go away than to burden the developer with it. (A similar line of thought applies to memory management. Better to have a good garbage collector than burden the developer. [See note 1.])
I suggest that it is better to implement some sort of module, package or class modifiers that require code to use Optional instead of null, and eliminating nulls altogether. If code can't generate a null, and can't receive a null, then there will be no nulls, and no need to implement ? and ! everywhere. This makes the most sense if/when Optional itself is a non-nullable value type that is elided from memory wherever it can be and flattened when it can't. And by implementing modifiers that are used as required, programmers who have continuing need of nulls for whatever reason can continue to use it.
Java supports Optional today, but it is not 'the standard' for handling null cases. Before Valhalla, I don't think it made sense to make Optional the standard because very Optional is one more memory allocation and one more indirection to have to deal with. Even while the compiler does its best to avoid short lived objects reaching the heap. With Valhalla starting to deliver, it will soon the the case that no significant penalty applies. Adopting Optional as the standard for the null case has years of comp science principles and industry practice behind it showing only good things so I don't think there is any longer much risk in following suit.
A decade ago Java modernised to great effect by adopting a form of functional programming. Until then Java was a traditional OOP language. The modern Java style is a mixed style including functional elements that programmers are already familiar with, and that appears to make Java an extremely effective programming language. Optional is already part of Java, programmers are already used to the functional part of Java's mixed style, and Optional fits really well. Stylistically Optional is a no-brainer. Adopting it as the standard for nulls seems likely to me to be a relatively small step for most.
In my opinion ! and ? will make Java code more complex to read and write. From "JEP draft: Null-Restricted and Nullable Types (Preview)" (https://openjdk.org/jeps/8303099):
Foo?[]!
Predicate!<Foo?>
I am opposed to this syntax. I don't want to pour over lines of code examining nullness markers to try to figure out what is going on. I think it is obvious that ! and ? burden the developer.
Java APIs need to remain compatible with legacy code. Perhaps some sort or auto-(un)boxing (to Optional) can be implemented so that legacy code and null-free code can call the same APIs in those cases where parameters are permitted to be null. [See note 2.] Name clashes would need to be addressed of course. Nulls return values could be similarly auto-(un)boxed.
The nullness markers ! and ? are also not going to help with non-nullable value types and the associated sought-after compactness in memory that can be achieved with such types. With non-nullable value types the fact of non-nullability is a property of the type, not of the code using the type. It can't be anything else since a non-nullable type must always have a value of some sort necessitating a default constructor.
I use a lot of non-nullable value types and the non-null requirement that supports memory compactness comes at a high cost. I don't think most programmers realise that. This is because to achieve compactness, null itself needs to be removed, but the requirement for representing the null case does not go away. For each non-nullable value type I have found it necessary to define at least one value of the type to represent not-a-value. This is usually the default value that is generated by the type's no-arg constructor. It is then useful to have a method for determining if an instance represents a value, or not-a-value, e.g. boolean isValue(). For every method implemented on such a type, except the isValue method, it is then necessary to check for validity and throw an AttemptedUseOfNonValue exception if the instance is equal to the not-a-value value. In type hierarchies, it has often been necessary to define a subtype that is NotAnXXX to represent the not-a-value case, and all of the methods of this subtype (except isValue) automatically throw the AttemptedUseOfNonValue exception. The values representing the null case for non-nullable value types also litter switch statements. That is to be expected. The null case _is_ a case.
I have included the comments on compactness to make it clear that while nullability and compactness are both about how null cases are represented, they are in fact very different problems and they can and do have very different solutions. Solutions that are decoupled from one another in terms of how a programmer works with them.
_In summary I suggest_:
• Regarding nullability
• Eliminate nulls in favour of Optional.
• Do not introduce the nullness markers ! and ?.
• Add modifiers to ask the compiler to enforce use of Optional and eliminate nulls.
• Use Optional boxing/unboxing to permit null-free and legacy code to use the same APIs as far as possible.
• Regarding compactness:
• Implement non-nullable as a class modifier at least for value classes.
_Concluding remarks_:
I am a Java programmer with a mixed style, not a pure functional programmer. Even so I largely avoid nulls by using Optional, sealed types, and other techniques. My experience suggests that the easier, more developer friendly, more Java compatible approach is to enforce use of Optional and eliminate nulls altogether rather than embark on the ! and ? route to cope with them.
Best wishes,
Jeremy.
Note 1: With Java's almost miraculous modern garbage collection, value classes and the possibility of flattening coming to Java, other approaches look irrelevant in an increasingly large number of situations.
Note 2: Going forward new APIs should not accept nulls. In general a method with optional parameters indicates that the method has more than one use case, which is usually better handled by method overloading or by using an interface object that with optional parameters. Legacy is one thing, but I don't think we should see much use of Optional in method parameter lists going forward.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20251113/9e2b981a/attachment.htm>
More information about the valhalla-spec-observers
mailing list