Meeting today: IdentityClass

Dan Smith daniel.smith at
Thu May 6 16:40:19 UTC 2021

> On May 5, 2021, at 8:39 AM, Remi Forax <forax at> 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.

You're correct that this is a risk. I don't think it's a deal-breaker—there aren't that many use cases for using 'getInterfaces', and those that do should ideally be writing code that isn't hostile to new supers—but I agree it's something to watch out for. The current plan is to release a preview feature and get some feedback on how disruptive this is.

> 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.

That's interesting. In this case, less of a concern because IdentityObject doesn't specify any behaviors at all. It's just a marker. If you're confused about, say, 'equals' behavior for primitive objects, you can go to the Object.equals documentation to get an explanation.

Object also has this property—you extend it even if you don't mention it in source. And I think that works just because "everybody knows" that's how it works. Understanding which class declarations implicitly implement IdentityObject and what class declarations implicitly implement PrimitiveObject will similarly be something that people just need to understand as part of their language fluency.

(If you're browsing javadoc, we'll be sure to make this obvious, just like it's obvious in javadoc that everything extends Object.)

> 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).

As John mentioned yesterday: the simplest solution is to make sure erasure treats 'K extends Object & IdentityObject' differently than 'K extends IdentityObject'. A bit awkward, but workable for authors who need to preserve binary compatibility.

> 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.

There are a few different pieces any solution should support (unless we're changing our requirements):

- Dynamic test for "is this object an identity object?" or "is this class an identity class?"
- Static type for a variable ('void m(IdentityObject o)')
- Type variable bound ('T extends IdentityObject')
- Concise way to constrain a class's subtypes to primitive/identity classes (both compile-time and runtime)

If you say IdentityObject is only a compile-time type, you've lost the dynamic tests, runtime guarantees about variable types, and runtime class subtyping restrictions. You haven't improved anything about javadoc discoverability.

Of course we could come up with other solutions for those problems, but that's a lot of complexity just to make life easier for 'getInterfaces' clients and library authors who want to compatibly apply IdentityObject as a bound. Doesn't seem like a good trade-off.

More information about the valhalla-spec-observers mailing list