Yes!
This changed during the long development of Project Valhalla.
As of 2024, the value types proposal, JEP 401, which is probably now very close to the final form, has value types as nullable:
Value class types are reference types. In Java, any code that operates on an object is really operating on a reference to that object; member accesses must resolve the reference to locate the object (throwing an exception in the case of a null reference). Value objects are no different in this respect.
But why?
It's not ideal that value types are nullable, because it adds an extra possible state to the type representation, which both makes it harder to reason about the state of your data and means that even when a value field is "flattened" into another object at runtime, it still needs an extra bit to store whether or not the "reference" is null. The VM work is truly complicated.
The reason it ended up like this, in short, is compatibility. There are many existing classes which are morally value types, like Integer
, Long
, Double
, LocalDate
, Optional
, any number of little immutable data carriers out there. But three decades of Java existing code will already do things like Integer value = null;
. Making value types non-nullable would have prevented the migration of these classes. My understanding is that early attempts at non-nullable value types ("primitive classes") also played havoc with generics and required a new complicated set of bytecodes and type descriptors to cope with the new types.
As it is, the model they've ended up with provides fantastic compatibility. Adding (or removing) the keyword value
in front of a class or record is a mostly binary compatible change, meaning that code that uses the class can be compiled separately from the value class itself. You mostly don't need to know whether the class you're using is a value class or not. No new bytecodes, nor type descriptors, nor constant pool forms. Migration becomes easy. The proposed diffs to the language spec and VM spec are reassuringly small.
So what is a value class?
As best as I can explain it, a value class is a class whose instances are described purely by the values of their fields. Its fields are final, it has no synchronized
lock/monitor (because that would need a hidden state variable), System.identityHashCode
is computed based on its field values (because there is no hidden hash code field), and ==
compares instances of value classes by comparing the bit value of each field.
(You can still have .equals
and ==
that differ. For example, if you make a value class with String
fields, you should provide a .equals
method to compare the Strings with .equals
, whereas ==
would merely check if the fields point at the same String instance.)
The opposite of a value class is an identity class. An identity class is a classic Java class where each new
instance lives somewhere distinct in memory, and ==
compares the memory address.
The slogan of Project Valhalla is "Codes like a class, works like an int". For example, imagine:
int a = 3, b = 3;
We don't have to ask "is this 3 the same 3 as the other 3?" The question doesn't make sense. There is only the value 3. There is no individual identity to "each" 3. So of course a == b
must compare them as equal. It can't compare identity (the way it would with new Integer
) because an int
primitive doesn't have identity, it only has value.
So, given some value class:
value record Point(int x, int y) {}
it will be the case that:
new Point(3, 4) == new Point(3, 4) // true!!
Which is obviously absolutely impossible in the pre-Valhalla times. They are separately created instances, but totally indistinguishable. (You could pretend that all possible values of a value class pre-exist somewhere as singletons and new
just gives the pointer to the cached instance.)
But null is, yes, still a possible value of the type:
Point pointless = null; // 👍
In the language model, value types are still nullable reference types, just not identity types.
What you get in exchange for giving up object identity is excellent performance. The early access releases warn not to use them for benchmarking, but I did, and I was ecstatic. When the VM knows it doesn't need to keep track of individual objects, it can aggressively shred them into naked fields passed around in CPU registers (plus a boolean for isItNull
). It beats the hell out of escape analysis.
I wholeheartedly encourage everyone to watch the July 2024 talk by Brian Goetz: https://www.youtube.com/watch?v=IF9l8fYfSnI
Nullity control
A closely-connected part of Project Valhalla is exploring null-restricted types, in which Java types (value or not) can be decorated with !
or ?
to specify whether or not they are nullable, at the point of use. For example, a variable of type String!
("string bang") is a reference to a String
and is enforced non-null. Obviously, this will marry well with value types. So types int
and Integer!
will have exactly the same possible values and become essentially the same thing: non-null pure 32-bit integer. Meanwhile, int?
would mean "nullable int".
The nullity control feature is frontier language development and still evolving.
null
is a value of reference type. Inasmuch as the whole point of JEP-169 value types is that they are not reference types, no,null
cannot be assigned as the value of such a type. At least, not without specifically attributing special-case significance to such an assignment. – Discoverernull
means nothing, so a value type representing nothing is conceivable. I know that the traditional meaning ofnull
is a reference that points to nowhere. Do you know if the sentencenull
is a value of reference type (or a similar one) is somewhere in the JLS? – Reversiblenull
" (JLS9, 3.10.7; formatted as in the original). – DiscovererNULL
to astruct
variable didn't make any sense. Why would it mean anything different now? Anyways, I think that my real intention with this question is to know if a value type can represent an empty/zero/void/nothing value, and how to implement that behavior, i.e. a number could be0
, a string""
, a location could be Greenwich, etc – Reversiblememset(myStructVar, 0, size)
and that filled my value type with all its bits set to0
. In modern C there are other ways to do the same, i.e.struct MyStructType myStructVar = {0};
. I believe this is what I want to know, i.e. if we'll have a general way to initialize value types to their default values in Java... Maybe I should post another question? – ReversibleInteger i;
for example ? – Cimbri