Value type hash code
daniel.latremoliere at gmail.com
Thu Apr 19 10:38:44 UTC 2018
A value type is explicitly immutable by design and not by documentation,
then equals/hashcode methods needs to have the same immutable behaviour
not just by documentation but also by design. These methods can not be
delegated to user (because it would allow value type design bugs in
immutability if hashCode change when user implement hash code with a
random function or by following a reference to a mutable object.
The only really immutable available information for a reference field is
System.identityHashCode(), then it need to be used.
NB: A value type is (for me) pure data designed for performance: it can
be a pair of integers, but it can not be a fraction. If you want
comparison behaviour of a fraction, you can do a fast equalsAsFraction
method by cross multiplication, but you can not implements fast and
correct hash code (you would need to simplify the fraction to have no
common divider between numerator and denominator).
Le 19/04/2018 à 10:04, David Simms a écrit :
> Summary of points raised:
> * Implementation specification: besides the general contract ,
> implementation doesn't need to be specified
> o this has advantages to JDK and JVM developers to enable change,
> protects users from said future changes
> * Default implementation
> o Javac could provide the default implementation
> + bloats class file forever
> o BSM mechanism described by John enables more flexibility,
> efficiency and better optimization opportunities
> + e.g. BSM may read annotations at lookup time, allowing users
> to decoratively specify which fields and which method for
> handling references
> + may have bootstrapping issues, if so, said JDK classes need
> to implement hashCode themselves
> o Even if the JVM doesn't implement it directly, it shouldn't
> crash or behave erratically
> + JVM: Return 0, -1, 4711, or throw exception (doesn't matter
> given the point above, 0 for argument's sake) ?
> * On the topic of calling reference fields, calling "hashCode()" or
> using "System.identityHashCode()"
> o "System.identityHashCode()" is consistent with
> "Arrays.hashCode(Object)" 
> + Almost meaningless to the user, many think it is a mistake
> o Calling "hashCode()" is consistent with
> "List.hashCode(Object)" 
> + may result in recursion or costly traversal
> # This is fine, user needs to decide what to do by
> supplying their own...
> o BSM method can help user to declare what they prefer
> Obviously a similar discussion can be had for "equals()", except this
> issue doesn't really involve the JVM (as hashCode does).
> Clearly being able to declaratively control hash/equals deep vs
> identity is very powerful...we'll be prototyping looking for further
> technical issues.
> Feel free to call shenanigans if I have something wrong. Agreeing to
> disagree is also an option, and nothing is set in stone, still
> Thanks for all the feedback !
> /David Simms
Just for history, my response to Rémi's mail last week on this subject
in amber (forbidden because discussion is not allowed on the
corresponding mailing list, and his following copy  of his mail was
on a reserved mailing list, then I had administrative phobia):
-------- Message transféré --------
Sujet : Re: Record design (and ancillary fields)
Date : Sun, 15 Apr 2018 06:19:44 +0200
De : Daniel Latrémolière <daniel.latremoliere at gmail.com>
Pour : Remi Forax <forax at univ-mlv.fr>
Copie à : amber-spec-comments <amber-spec-comments at openjdk.java.net>
Le 14/04/2018 à 23:18, Remi Forax a écrit :
> I do not think we have to do something specific for supporting
> relational database mapping,[...]
I'm not asking for object-relational mapping, only for not forgetting
experience from database design.
MapReduce in Google index database is not the same than map/reduce in
java.util.stream, but they are the same design pattern.
>> PS: Given primitive/value type disallow cyclical references, this will
>> prohibit StackOverflowException in equals/hashCode methods.
> only if an equals on a value type that contains an object doesn't call
> equals on that object.
Another design would probably be a bug in these compiler generated
methods for value type: value types are using pass-by-value convention
They are like primitives (boolean, int, float, ... and address!). For a
value type, a field targeting an object is opaque, value type know only
address, not pointed object.
From point of view of compiler, equals/hashCode methods of value types
would be using all fields but these fields can only be primitive or
value types. Then, recursion is only possible between value types, and
always descending, then finite (like Fermat). After flattening all
levels of value types inside a value type, it will become non recursive
and using only primitives.
Given pass-by-value design, these generated methods would be required to
be defined like a field containing an address value (not a field
targeting an object):
- equals use identity test on the field (not equality test).
- hashCode use result of System.identityHashCode on targeted object (not
hashCode virtual method, which would also create potentially
NullPointerException in case of null address if not tested before).
More information about the valhalla-dev