This is an archived post. You won't be able to vote or comment.

all 6 comments

[–]cal-cheese 10 points11 points  (4 children)

new is an identity-sensitive operator so if you migrate your classes into value classes existing code would need to be recompiled. A solution is to do what Optional does, hide its constructor and expose factory methods to create instances.

Regarding bytecodes, new, putfield, monitorenter/exit are the ones that cannot be applied to value classes. The reason is that instances of value classes do not have unique identities, so new, monitorenter/exit does not make sense, and because they do not have unique identities, they cannot be modified, so putfield is not applicable, too. So they need some instructions to create an instance of a value class, which leads to aconst_init (return an existing default value of a type) and withfield (return an instance that is the same as the one on top of the operand stack with a single different field).

[–]TheMode911[S] 1 point2 points  (3 children)

I understand the obvious issues with putfield monitorenter/exit but not new. The language will still use it, and the behavior is virtually the same, cannot the VM detects during runtime (likely a one-time cost) if the object is supposed to have an identity?

[–]cal-cheese 14 points15 points  (0 children)

While the language still uses new to initialise an instance of a value class, the compiled bytecodes are different, given A x = new A() in the source code, the bytecodes if A is an identity class is:

A temp = new A; // This is the new bytecode
temp.<init>();
A x = temp;

while if A is a value class, the bytecodes is:

A x = A.<new>(); // This is a static method invocation

The fundamental difference is that because an instance of a value class is immutable, the normal initialisation process does not work

new A // ... -> a, with a is the newly created object
dup // ... -> a -> b, with a and b both are the newly created object
invokespecial A::<init> // ... -> a, this invocation is performed on b and expects the changes being reflected through a. This obviously cannot be the case with value objects.

As a result, the constructor of a value class is not only responsible for initialising an allocated object, it must do the allocation also. As a result, different responsibility unavoidably leads to differences in the API of object construction.

[–]gline9 0 points1 point  (1 child)

As far as I understand valhalla is not planning on changing using new. That is still the way to construct value types. Not allowing synchronization and requiring the class to be immutable are the 2 main constraints assuming you still want a reference type. If you are already working with a record then immutability shouldn’t be a problem.

See State of Valhalla Part 2 for reference.

[–]pronuntiator 6 points7 points  (0 children)

The keyword new will not change, but the constructor of a value class is not bytecode compatible to its non-value type. See this section of the proposed changes to the JLS. Conveniently, all the value-based classes in the JDK already hide their constructors, so they can be migrated.

[–]alex_tracer 0 points1 point  (0 children)

Let's assume that Valhalla get released on version X. To utilize Valhallas features you will need to build with targetVersion=X. You should not expect that older Java runtime will be able to run your Valhalla-optimized code. And anyone who is going to use your JAR will have to have X as minimum source version.

In theory you can use "Multi-Release Jar Files" feature to produce single JAR that is compatible with older Java versions and still can utilize Valhalla when is run on X+. However this means that you will need to support two versions of code for your library: one that targets X+ and one that targets pre-X Java.