The interfaces IdentityObject and ValueObject must die !

Dan Smith daniel.smith at oracle.com
Thu Jan 27 00:36:30 UTC 2022



On Jan 26, 2022, at 4:36 PM, forax at univ-mlv.fr<mailto:forax at univ-mlv.fr> wrote:

But this isn't a property of the *class*, it's a property of the *type*, as used at a particular use site. If you want to know whether an array is flattened, the class of the component can't tell you.

The semantics B1/B2/B3 is a property of the class, an instance of a value class stored in an Object is still an instance of a value class, same for primtive classes.

Being flattenable or not, is an orthogonal concern. Being flattenable for a field or a local variable/parameter it's a property of the descriptor, but for arrays it's a property of the array class because arrays are covariant in Java.

For arrays, a code like this should work
  Object[] o = new QComplex;[3];
  o[1] = QComplex;.new(...);

My point is that if you're holding an Object, there is nothing interesting about instances of primitive classes to distinguish them from instances of value classes. They are both value objects, with value object semantics.

Now, if you're holding an Object[] and you want to know whether the array is flat or not, that could be a useful property to detect. But a query like the following won't tell you:

PrimitiveObject.class.isAssignableFrom(arr.getClass().getComponentType());

Instead, the question you want to ask is something like:

arr.getClass().getComponentType().isPrimitive();

You're claiming that the IdentityObject vs. ValueObject and value class vs. primitive class distinctions should be treated the same, but what I'm illustrating here is that they are different kinds of properties. The first is a property of a class (or of all instances of a class), the second of a type (or a container with a type).

I don't understand why you want Point.ref/LPoint; to be a PrimitiveObject and not a ValueObject.
We have QPoint;[] <: LPoint;[] so LPoint; implements ValueObject and QPoint; implements PrimitiveObject.

Classes implement interfaces. Types have subtyping relationships *derived from* classes. The mechanism of encoding a property with an interface thus involves attaching that interface to a particular class.

So if you want QPoint <: PrimitiveObject, it must be the case that *class* Point implements PrimitiveObject. And then it will be the case also that LPoint <: PrimitiveObject.

(I don't want any of this. What I want is for there to be just one interface, ValueObject, that indicates identity-free semantics for a class/object, and I want class Point to implement that interface.)

Meanwhile, I'd suggest writing the method like this, using universal generics:

<T>  public void m(T[] array, int index) {
   array[index] = null;  // null warning
 }

If you are Okay with code that can raise a NPE, why are you not Okay with code that can raise an IllegalMonitorStateException ?
Or said differently why ValueObject and not PrimitiveObject.

You've lost me here.

I showed an example that uses universal generics. The type variable T ranges over both primitive and reference types, so may not be nullable. It is improper to write a null into a T[]. As a concession to compatibility, we want to treat this as a warning rather than an error, because plenty of code like this exists today.

Similarly, there may be code that ranges over both value objects and identity objects. An easy way to do this is with a variable of type Object. It is improper to synchronize on an Object that may be a value object. Again, as a concession to compatibility, we probably want to treat this as a warning.

public void m(Object obj) {
    synchronized (obj) { // warning
        ...
    }
}

These are both situations where being able to detect properties of the input could be useful to avoid runtime errors, but that doesn't mean the mechanism should be the same for both. (And I explained above why testing for PrimitiveObject[] wouldn't do what you want it to do.)

Object can't be an identity class, at compile time or run time, because some subclasses of Object are value classes.
Object the type is not an identity class, but Object the class (the Object in "new Object()") is an identity class.

You're envisioning a different model for types and classes than the one we have.

An instance of a class is also an instance of (and carries the properties of) its superclasses. Value objects are instances of the class Object.

I can imagine a design in which we say that instances of Object may be either identity or value objects, but direct instances of the class are always identity objects. But this is not how we've handled the property anywhere else, and it breaks some invariants. We've gotten where we are because it seemed less disruptive to introduce a subclass of Object that can behave like a normal identity class.

(I think we can explore this more, though, and note that I didn't say anything about interfaces above. It's orthogonal.)



More information about the valhalla-spec-observers mailing list