-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8336043: Add quality of implementation discussion to Object.{equals, toString, hashCode} #20128
base: master
Are you sure you want to change the base?
Conversation
…ls, toString, hashCode}
👋 Welcome back darcy! A progress list of the required criteria for merging this PR into |
❗ This change is not yet ready to be integrated. |
* collections}. | ||
* | ||
* As as a quality of implementation concern, a particular | ||
* implementation of this method may or may not support generating |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"may not support generating hash codes" sounds weird; maybe like "may or may not guard against cyclic data structures in recursive hash code generation."
I think the key is "recursive" here, as only recursive implementations are at risk of these faults.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, recursion is the easiest way to accidentally write an infinite loop, but I think a few other ones would be possible too ;-) I'll considering updating the wording to highlight the most likely hazards; thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the typical implementation pattern is to recur through the component fields of the object. Not recursion through this
but rather the hashCode implementation of some object will typically invoke hashCode of its component fields. Of course this can result in infinite recursion (really, StackOverflowError) if the object graph contains cycles, but in general, implementations of these methods don't guard against this possibility. (Nor should they. Adding such guard code seems impractical.)
Note that the default implementations of these methods for records and for Collection classes doesn't guard against cycles. (The collections contain some special cases for collection that contains itself, but not cycles in general.)
I have a general concern about advice that isn't actionable, such as "may not support" or "care should be taken" etc. If we really want to say something about cyclic data structures, maybe we could say something like this. For data structures that may contain cycles, these implementations should avoid traversing component fields that may lead to cycles. A class could simply inherit the method implementations from Object. Alternatively, the class could contain a unique ID that's used for determining the result of equals/hashCode/toString. (Of course, determining uniqueness is up to the application.)
Webrevs
|
* to provide reasonable implementations of these methods. In | ||
* particular, the implementations should take care to avoid using | ||
* excessive memory, computational time, or any other resources. | ||
* Additionally, during typical operation these methods should |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A note on terminology, I used "typical" for the new notes rather than "normal" to avoid conflating the discussion with the JLS concept of code "completing normally" (as opposed to completing abruptly due to an exception):
https://docs.oracle.com/javase/specs/jls/se22/html/jls-14.html#jls-14.1
* maintenance domain, any overrides of these methods should take care | ||
* to provide reasonable implementations of these methods. In | ||
* particular, the implementations should take care to avoid using | ||
* excessive memory, computational time, or any other resources. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it's not wrong, advising against "excessive" usage is so vague as not to be actionable. Given some piece of code it's hard to say whether or not it does anything excessive. Consider a List with a million elements; as a practical matter, any one of these methods requires visiting every element. Is that excessive? Why or why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it's not wrong, advising against "excessive" usage is so vague as not to be actionable. Given some piece of code it's hard to say whether or not it does anything excessive. Consider a List with a million elements; as a practical matter, any one of these methods requires visiting every element. Is that excessive? Why or why not?
Hmm. In rare cases, I think providing not-quite-actionable advice is on net helpful.
My basic goal here is to convey "when you're implementing these methods, be considerate to your expected users." If that results in some extra contemplation during development or code review about whether or not the worst-case behavior of hashCode/toString/equals is excessive, I think that would be a reasonable outcome from this change.
* <em>not</em> throw any exception or other throwable; as always, a | ||
* {@link VirtualMachineError} is possible during the execution of a | ||
* method, often due to factors outside of the method's direct | ||
* control. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Should not throw any exception or other throwable" is overly broad. However, there is a narrower sense where code that implements these methods "shouldn't" throw anything. I'd suggest focusing on precondition checking. Specifically, no object should ever be in a state such that calling one of these methods results in IllegalStateException or other exception based on the state of the object. In addition, no argument passed to equals() should ever cause IllegalArgumentException, ClassCastException, NullPointerException, or other exception based on the argument.
(This comment applies to other locations where the "excessive" wording is used.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would hope to spend as little space on this as possible, perhaps "This method should avoid throwing or propagating any exceptions unless it legitimately cannot adhere to this contract."
(or shorter)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would hope to spend as little space on this as possible, perhaps "This method should avoid throwing or propagating any exceptions unless it legitimately cannot adhere to this contract." (or shorter)
Pushed a version using that worked, but I expect discussion will continue.
* typical operation. In particular, {@link ClassCastException} | ||
* should not be thrown if the argument has an incomparable type | ||
* to this object and {@link NullPointerException} should not be | ||
* thrown if the argument is {@code null}. The implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these cases the recommendation should be to return false instead of throwing such exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point; updated accordingly.
@kevinb9n You should take a look at this. |
Here's a classical example of a directed acyclic graph (DAG, no cycles) consisting of 100 This is to say that even without cycles and even with quite small data structures as here, we might encounter excessive resource usages in space or time by just invoking these methods.
|
Bloch's Effective Java, 3/e, Items 10, 11, and 12 have some useful background information on implementing equals, hashCode, and toString. |
Agreed; I reread over those items before posting the PR. The "typical" wording was meant to cover guidance like Bloch's "have equals throw an AssertionError if the method should never be called if it escapes its intended domain." |
* in the context of {@linkplain java.util##CollectionsFramework | ||
* collections}. | ||
* | ||
* As as a quality of implementation concern, a particular |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"As as" typo.
"particular" is unnecessary. "a" -> "an"
@jddarcy This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
Keep alive. |
@jddarcy This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
Keep alive again. |
@@ -166,6 +208,7 @@ public Object() {} | |||
* argument; {@code false} otherwise. | |||
* @see #hashCode() | |||
* @see java.util.HashMap | |||
* @see ##objectOverrides Guidance for overriding {@code Object} methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this rendered as code font? I think only <
-starting text is not rendered as code font.
First pass at adding some quality of implementation discussions around the overridable methods of Object.
Progress
Issues
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/20128/head:pull/20128
$ git checkout pull/20128
Update a local copy of the PR:
$ git checkout pull/20128
$ git pull https://git.openjdk.org/jdk.git pull/20128/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 20128
View PR using the GUI difftool:
$ git pr show -t 20128
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/20128.diff
Webrev
Link to Webrev Comment