[External] : Re: New candidate JEP: 401: Primitive Objects (Preview)
Brian Goetz
brian.goetz at oracle.com
Tue Apr 20 18:46:57 UTC 2021
> Let me add that I don't think that primitive classes will be a
> frequent case for a standard developer. 98% of Java developers will
> probably never be tempted to ever write their own primitive classes. I
> see this as a feature only for some rather low-level types, like
> Optional or some of the java.util.time classes. I can hardly imagine
> much more, even including all the standard frameworks - only some
> mathematical libraries and some internal classes in collection
> frameworks will make use of it. Or the disruptor framework. But there
> the benefit is huge!
I hope this will be true, but I'm also aware that developers don't
always do what you expect them to. Developers can be inordinately
attracted to "performance" features; if some blog author says "primitive
classes are faster" (which surely will happen), that may motivate a lot
of developers using them, even when they shouldn't, at least for the
first few years until more universally-accepted "best practices" emerge.
> But at the end, it's a matter of opinion, and I understand your
> concerns against such an "awkward look" for primitive class names.
FWIW, in the early days of OO languages, we saw a lot of conventions
like "interface names begin with `I`" and "field names begin with `f_`.
I'm glad that burned itself out before Java came along.
Also FWIW, one of the considerations when choosing this was: in the
difference between "Point by value" and "Point by reference", which is
more important to readers, Point-ness, or val/ref-ness? We deemed it to
be the data type, which is why that comes first and the carrier marking,
if any, comes last.
>
>>
>> Fourth, while this reduces the chance that a user will mistake a
>> primitive class instance for a reference class instance, the cost of
>> this is that APIs become, from the perspective of many users,
>> gratuitously inconsistent. Having some classes called "account" and
>> others called "AccountGroup" will also be a persistent irritant.
>
> You will find examples where this is true, but I don't buy this one.
> ;) I can't imagine why someone would want to declare the domain class
> 'Account' as primitive? Especially when other classes in the same
> model are not. If someone is mixing primitive and reference types in
> the same model together, then he or she is doing something very wrong.
> I would be curious if there is a useful real life example for such a
> use case, but I can't find one.
I think it goes back to the "performance is (over)important" thing. I
can easily imagine developers will declare the classes that can be
primitives, as primitives, only using identity where they need to.
Whether this is good or bad is another question, but I suspect it will
happen. (We see this today with enums and records; where people can use
the more restricted form, they do, because of the benefits. We see this
as normal, in part because we've set up enums and records as "just being
classes".)
> And even if they do so: It's not much better with the current
> proposal. If you ask the account for its groups, you get instances of
> AccountGroup, but if you ask the group for its accounts, you get
> instances of Account.ref. That's also irritant.
No, that's not right. If I have:
Point.val p1 = ...
Point.ref p2 = p1
then `p1 == p2` and `p1.getClass() == p2.getClass()`. The ref/val is
just the "envelope"; the enclosed object is the same either way.
>
>>
>>> * The suffixes .ref and .val don't fit into our concept of class
>>> names, they look ugly and can easily be mixed up
>>
>> I'm really glad you brought this up, because it's a common
>> misperception.
>>
>> [Some good context here]
>
> Thanks for the insight. And to be honest, I have no concerns against
> the .ref suffix; one would rarely explicitly use the reference type
> when the value type could also be used, and if so, it's perhaps a good
> idea to mark this more prominent. Also primitive types will rarely be
> used in standard collections, I assume. So an argument for the suffix
> style.
>
> what I find sad is that we're in the need for the .val suffix. But
> that better explained in the next section.
Yes, I am sad too! But realistically, this _will_ be rare, because
relatively few classes will get migrated. (Probably 90% of the pain
will be with Optional.) But there's a principle here, too: make the
migrated classes carry the cost of migration, so that all-new code
doesn't have to. In the all-new-code world, you never say .val except
in very strange circumstances.
> So there are two distinct naming schemas for the same kind of
> reference/value pairs. The only difference is that the one class was
> introduced before the JEP, and the second one after that.
Close -- the difference is that the class was _migrated to a primitive
class_. One can imagine classes being born identity classes after this
JEP, and still migrating. Not all classes will migrate on a special
Migration Day. But yes, your point stands, if the class was born
differently, it gets the reversed naming convention. Still, most of the
time, in either case, you can just say Optional, and the system will do
something reasonable. Only in the conjunction of (a) migrated types and
(b) performance-obsession will you tune the envelope of each declaration
explicitly.
> If there wouldn't be a 'float' primitive yet, we would call the class
> Float and the boxed type Float.ref. Or if it would've been introduced
> a bit earlier, it would have been Float.val and Float. But it's not,
> the primitive name if float and the boxed type Float, just as in my
> proposal.
>
> What I read is that you plan to define aliases for, e.g., float that
> refers to Float.val. This wouldn't be necessary if the naming scheme
> would already cover the existing pattern of primitive types. Except
> int and char which are falling out a bit.
>
> So the idea is avoiding three different naming schemes for the same
> concept.
>
> And if someone want to invent a type for imaginary numbers, they can
> call the class imaginary, the boxed type is Imaginary, and it fits
> very well into the existing primitive types. It just feels similar.
This is the subject of JEP 402. Currently we have eight primitive / box
pairs (of which two have the unfortunate characteristic that the name is
not even the same modulo case of the first character, Integer and
Character.) The plan is to:
- migrate the box classes to primitive classes;
- define int.ref to mean Integer;
- define Integer.val to mean int.
This way, the only special thing is that these legacy classes have
ad-hoc aliases; int and Integer.val are the same type, and int.ref and
Integer are the same type. Clearly we can't get rid of these names,
they're too pervasive, but we can tame them a bit.
Cheers,
-Brian
More information about the jdk-dev
mailing list