-
Notifications
You must be signed in to change notification settings - Fork 435
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
Standardize #equals(Object) formatting #197
Standardize #equals(Object) formatting #197
Conversation
Click here to submit a new iteration when this PR is ready for review. See this repository's contribution guide for more information. |
Codecov Report
@@ Coverage Diff @@
## master #197 +/- ##
============================================
+ Coverage 74.03% 75.37% +1.34%
+ Complexity 423 419 -4
============================================
Files 71 71
Lines 1290 1332 +42
Branches 127 126 -1
============================================
+ Hits 955 1004 +49
+ Misses 301 298 -3
+ Partials 34 30 -4
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
v1@Eclipse-Dominator submitted v1 for review.
Checkout this PR version locallygit fetch https://github.com/se-edu/addressbook-level3.git refs/pr/197/1/head:BRANCHNAME where |
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.
@Eclipse-Dominator let's include all applicable commits of se-edu/addressbook-level4#973 and not add any other unrelated commits.
When a PR does a bigger task in multiple smaller steps, we should use a merge commit (not a rebase merge, nor a squash commit) so that both steps and overall outcome are clear -- but we can't do that if there are unrelated commits in the PR.
@@ -1,5 +1,7 @@ | |||
package seedu.address.commons.util; |
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.
Use chatGPT to refine the commit message? I spotted some grammar errors.
@@ -16,6 +18,7 @@ public class ToStringBuilder { | |||
* Constructs a {@code ToStringBuilder} whose formatted output will be prefixed with {@code objectName}. | |||
*/ | |||
public ToStringBuilder(String objectName) { | |||
requireNonNull(objectName); | |||
stringBuilder.append(objectName).append(OBJECT_PREFIX); | |||
} |
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 don't think we need to check every parameter for null. It need to be used judiciously, to preempt cases where letting a null pass through can cause hard to detect bugs or cause other damage. There is no need to do null checks for cases where a null would cause an obvious and quick failure without affecting system integrity (e.g., without writing incorrect values to the database).
So, we need to think hard if this commit is needed.
@@ -14,6 +14,9 @@ | |||
public class NameContainsKeywordsPredicate implements Predicate<Person> { |
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.
Not related to this PR. Submit it as a separate PR.
e1c766e
to
0c8fd9d
Compare
v2@Eclipse-Dominator submitted v2 for review.
(📚 Archive) (📈 Interdiff between v1 and v2) (📈 Range-Diff between v1 and v2) Checkout this PR version locallygit fetch https://github.com/se-edu/addressbook-level3.git refs/pr/197/2/head:BRANCHNAME where |
@Eclipse-Dominator how does V2 differ from se-edu/addressbook-level4#973 ? |
It includes some additional standardisation of |
@Eclipse-Dominator I see. Confirmed these additional |
There are no additional The second commit adds some extra test cases for some of |
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.
Just a comment on the commit message. The rest seems good to go.
@@ -118,7 +118,6 @@ public class RemarkCommand extends Command { | |||
|
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.
The commit message can be short, as this change doesn't really require a justification.
The style of the #equals(Object) implementation varies
across the code base. Let's standardize it to improve the
consistency of the code.
Co-authored-by: Zhaoqi <[email protected]>
In general, avoid the following when writing commit messages:
- Using
should do X
as if it's a universally accepted rule. Often, it is just our choice, and there could be ways of doing it. Instead, explain it as what the commit doesLet's do X
. - Reversing the solution and stating it as a problem. e.g.,
the code doesn't omit X
(problem) ...let's omit X
(solution). Often the problem is not an exact opposite of the solution. e.g., Logger is giving a warning when it should not (problem) ... Let's omit X from checkstyle (solution).
@se-edu/tech-team-level1 for your review ... |
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.
Just a suggestion for your consideration (or amusement), perhaps the #equals
standardization could better follow the DRY principle.
A utility function could be added to commons.util.AppUtil
(or another shared class):
public class AppUtil {
...
public static <T> boolean isEqual(Class<T> clazz, T target, Object other, BiFunction<T, T, Boolean> resolver) {
if (target == other) {
return true;
}
if (!clazz.isInstance(other)) { // handles null
return false;
}
return resolver == null || resolver.apply(target, clazz.cast(other));
}
}
Then other classes could use it like so:
public class Phone {
...
@Override
public boolean equals(Object other) {
return AppUtil.isEqual(Phone.class, this, other, (p1, p2) -> {
return p1.value.equals(p2.value);
});
}
}
Not too sure of any downsides so any criticism is welcome :)
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.
LGTM. I have one small doubt regarding one of the methods but generally, the changes look good.
&& getAddress().equals(e.getAddress()) | ||
&& getTags().equals(e.getTags()); | ||
EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other; | ||
return Objects.equals(name, otherEditPersonDescriptor.name) |
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.
From my understanding, Objects.equals() considers 2 nulls to be equal. Although, in this case, the name field being null would have been caught already, perhaps it would still be better to use Object.equals() or the default equals method of the respective objects?
eg.
name.equals(otherEditPersonDescriptor.name)
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 don't think this will occur due to requireNonNull(editPersonDescriptor);.
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.
it's possible for the local name field to be null. User does not need to edit all the fields of an entry so it's possible to have name being null. In this case name.equals(...)
will throw nullpointer exception.
So Objects.equals is used here
Interesting idea @hansstanley I like how it avoids the repetition of the first two checks. One downside I see is that it goes from a very straightforward code to a more complex code. |
@@ -50,12 +50,13 @@ public boolean equals(Object other) { | |||
return true; | |||
} | |||
|
|||
// instanceof handles nulls |
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.
This comment might not be necessary.
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.
It's to standardize across all equals() so all of the equals() contains this comment.
k instanceof clazz
returns false if k is null is not a common way to check for null value of k so the comment is included there.
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.
LGTM, might need to clean up some unnecessary comments.
I think it's quite an interesting solution but I do agree with @damithc that having the current implementation might be better for students viewing the code. |
20b6dae
to
2b257b5
Compare
v3@Eclipse-Dominator submitted v3 for review.
(📚 Archive) (📈 Interdiff between v2 and v3) (📈 Range-Diff between v2 and v3) Checkout this PR version locallygit fetch https://github.com/se-edu/addressbook-level3.git refs/pr/197/3/head:BRANCHNAME where |
@@ -69,7 +68,6 @@ public boolean equals(Object other) { | |||
return false; | |||
} | |||
|
|||
// state check | |||
PersonCard card = (PersonCard) other; | |||
return id.getText().equals(card.id.getText()) |
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 we add a comment here to inform the reviewer that the 'Label equals()', the id , behavior differs from our 'equals()' method?
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.
@Eclipse-Dominator your thoughts?
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.
@Eclipse-Dominator your thoughts?
@Eclipse-Dominator reminder ...
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.
This method is currently not used in AB3 and does not really fit the idea of equal
, thus it's better off removed from the source code.
I have included the commit from AB4 that removes this method in v4
The style of the #equals(Object) implementation varies across the code base. Let's standardize it to improve the consistency of the code. Co-authored-by: Zhaoqi <[email protected]>
This method implements a very weak notion of equivalence, where as long as the PersonCard's person and id are equal, the object is equal. However, such a weak notion of equivalence goes against the spirit of the UI architecture, which states that all UiParts represent a distinct part of the UI, and more importantly, wraps a subgraph of the JavaFX scene graph (accessible via UiPart#getRoot()). As such, just because two PersonCards A and B happen to contain the same ID and person value, doesn't mean they are "equal", because they may be holding different JavaFX scene graph objects. Any JavaFX-related operations on PersonCard A may give a different result to PersonCard B. Furthermore, this implementation of PersonCard#equals(Object) isn't actually used, not even by test code. It was introduced in baa5988 (PersonListPanelHandle: Update methods, 2017-07-14) so that one could do the following to assert that a certain PersonCard was selected: private void assertCardSelected(Index index) { PersonCard selectedCard = getPersonListPanel().getSelectedCard().get(); assertEquals(getPersonListPanel().getCard(index.getZeroBased()), selectedCard); } where the assertEquals() would then call PersonCard#equals(Object). However, since 04a675f (PersonListPanelHandle: Update getSelectedCard(), 2017-07-20) this pattern of code has been (rightly) removed, with test code preferring to compare what is actually displayed on the PersonCard, rather than using PersonCard#equals(Object): /** * Asserts that {@code actualCard} displays the same values as {@code expectedCard}. */ public static void assertCardEquals(PersonCardHandle expectedCard, PersonCardHandle actualCard) { assertEquals(expectedCard.getId(), actualCard.getId()); assertEquals(expectedCard.getAddress(), actualCard.getAddress()); assertEquals(expectedCard.getEmail(), actualCard.getEmail()); assertEquals(expectedCard.getName(), actualCard.getName()); assertEquals(expectedCard.getPhone(), actualCard.getPhone()); assertEquals(expectedCard.getTags(), actualCard.getTags()); } PersonCard#equals(Object) was, however, not removed then. So let's remove it now.
2b257b5
to
ab8e0e8
Compare
v4@Eclipse-Dominator submitted v4 for review.
(📚 Archive) (📈 Interdiff between v3 and v4) (📈 Range-Diff between v3 and v4) Checkout this PR version locallygit fetch https://github.com/se-edu/addressbook-level3.git refs/pr/197/4/head:BRANCHNAME where |
Partially adapted from AB4#973
To go along with some of the equality checks, some methods and constructors are reevaluated to follow defensive programming by adding suitable null checks.
AB3 has all sorts of #equals(Object) flavors. Some of them are formatted
with nested logic comparison while some contain unnecessary code. It will
enable better readability for others if they were to be standardized to
follow a similar format.
Generally, #equals(Object) should:
Should the internal variable be nullable, we should use
Objects.equals(var, var2) to compare between the 2.
Doing the above should improve the clarity and ease of
use of #equals(Object) methods.