Meeting today: IdentityClass

forax at forax at
Wed May 5 19:53:51 UTC 2021

> De: "Brian Goetz" <brian.goetz at>
> À: "Remi Forax" <forax at>, "valhalla-spec-experts"
> <valhalla-spec-experts at>
> Envoyé: Mercredi 5 Mai 2021 20:20:07
> Objet: Re: Meeting today: IdentityClass

> Here are some idioms I can imagine a use for with IdentityObject:

> // parameter type
> void withLock(IdentityObject monitor, Runnable task)

> // type bound
> Map<K extends IdentityObject, V> map = ... // gonna lock on keys

> // dynamic check
> if (x instanceof IdentityObject) {
> sync (x) { task(); }
> }
> else {
> sync (GLOBAL_LOCK) { task(); }
> }

> // reflective check against class
> Class<?> c = ...
> if (IdentityObject.class.isAssignableFrom(c)) { ... }

> The "fake type" approach handles the first two cases well enough. But it doesn't
> address the latter two cases; for that we'd need to expose a
> Class::isIdentityClass (x.getClass().isIdentityClass()), which seems workable,
> though might feel gratuitously different from the first two.
Erasure in the generic sense (sorry for the pun) is the way to retcon types as if they were present since the beginning in Java. 
So it fit well with the idea of IdentityObject/PrimitiveObject. 

Now, i think there are two kind of erasures, we have classic erasure by the compiler only like with generics and we have erasure with TypeRestriction which involve the VM. 
Note that the erasure using TypeRestriction does not necessarily implies any parametric type, this is a separate mechanism. 

As you said, classic erasure only supports the first two scenarios, while i believe that erasure by TypeRestriction support all of them. 

> Erasing to Object means that we don't get to do things like overloading:

> m(IdentityObject o) { ... }
> m(PrimitiveObject o) { ... }

> or

> m(IdentityObject o) { ... }
> m(Object o) { ... }
Sadly, this scenario is not supported by both kinds of erasure. 

And as i said two weeks ago, erasure by TypeRestriction is also something interesting for value based classes, at least some of them, the ones that do not allow null, by example for Optional. 
Optional the Q-type can be erased as Optional the L-type everywhere in the existing method signatures so we have binary backward compatibility at the price of 
- having the VM to lightweight box/unbox at the inlining horizons and 
- spurious NPEs where a null is sent as an Optional. 

Obviously, this is an incomplete proposal, but i think it's worth to take a look into that direction. 


> On 5/5/2021 10:39 AM, Remi Forax wrote:

>> If it's possible, i would like to discuss about IdentityClass again.

>> As noted in the last draft, adding IdentityClass automatically at runtime is not
>> a compatible change if tests that uses Class::getInterfaces() are not written
>> correctly,
>> and sadly, a lot of tests are written that way. I'm guilty to having written
>> those tests too.
>> I don't believe that the solution is to tweak the reflection because adding
>> IdentityClass at runtime is already a hack and introducing a hack² (hack square
>> hack) is usually where we should stop before anyone sanity goes under the bus.

>> The purpose of IdentityClass is
>> - to have a classfile for the javadoc describing the behavior of all identity
>> class
>> - to be used as type or bound of type parameter.

>> Apart the result of Class::getInterfaces() being hardcoded, IdentityClass has to
>> other issues, as javadoc container, given that IdentityClass is inserted by the
>> VM, it means there is no place in the Java code where someone can click to see
>> the javadoc in an IDE, we have the same issue with java.lang.Record currently,
>> the class is added by javac automatically and unlike java.lang.Enum, there is
>> no method useful on java.lang.Record (equals/hashCode and toString are defined
>> directly on the record) so very few of my student where able to understand why
>> a record with a NaN value was equals to itself.
>> But there is a more serious issue, using IdentityClass is not backward
>> compatible with Object.
>> When we have introduced IdentityClass, one scenario was to be able to declare
>> that the type parameter corresponding to the keys (K) to only support identity
>> class.
>> This is not possible using IdentityClass because the erasure of K will be
>> IdentityClass instead of Object (IdentityClass also appears when the compiler
>> will to a lub, so the common class of String and URI will be computed as
>> IdentityClass instead of Object leading to source compatibility issues).

>> I think at that point, we should go back to our blackboard and see if there is
>> no other solution.

>> I see two, one is to do *nothing* and do not add a type saying that only
>> identity class is a corner case after all,
>> the other is to piggyback on erasure not unlike Scala does, i.e. IdentityClass
>> is a fake class that is erased to Object at runtime.

>> regards,
>> Rémi

More information about the valhalla-spec-observers mailing list