I think we should revisit the idea of having the interfaces
IdentityObject/ValueObject.
They serve two purposes
1/ documentation: explain the difference between an identity class and a value
class
2/ type restriction: can be used as type or bound of type parameter for
algorithms that only works with identity class
Sadly, our design as evolved but not those interfaces, they do not work well as
type restriction, because
the type is lost once an interface/j.l.Object is used and the cost of their
introduction is higher than previously though.
1/ documentation
- Those interface split the possible types in two groups
but the spec split the types in 3, B1/B2/B3, thus they are not aligned
anymore with the new design.
- they will be noise in the future, for Valhalla, the separation between
identity object and value object
may be important but from a POV of someone learning/discovering the language
it's a corner case
(like primitives are). This is even more true with the introduction of B2,
you can use B2 for a long time without
knowing what a value type is. So having such interfaces front and center is
too much.
2/ as type
- Being a value class or a primitive class is a runtime behavior not a compile
time behavior,
so representing them with special types (a type is a compile time construct)
will always be an approximation.
As a consequence, you can have an identity class (resp value class) typed
with a type which is not a subtype
of IdentityObject (resp ValueObject).
This discrepancy is hard to grasp for beginners (in fact not only for
beginners) and make IdentityObject/ValueObject
useless because if a method takes an IdentityObject as parameter but the type
is an interface, users will have
to cast it into an IdentityObject which defeat the purpose of having such
interface.
(This is the reason why ObjectOutputStream.writeObject() takes an Object as
parameter and not Serializable)
And the cost of introduction is high
- they are not source backward compatible
class A {}
class B {}
var list = List.of(new A(), new B());
List<Object> list2 = list;
- they are not binary backward compatible
new Object().getClass() != Object.class
- at least IdentityObject needs to be injected at runtime, which by side effect
requires patching several
VM components: reflection/j.l.invoke, JVMTI, etc making the VM spec more
complex that it should be.
You may not agree with some of my points, nevertheless, the current design is
not aligned with the B1/B2/B3 world, have a high cost and with little benefit
so i think it's time to remove those interfaces from the design.
regards,
Rémi