I'm reading J. Bloch's effective Java and needed to brush up the equals/hashCode contracts and relationships.
I have the following JavaBeans class:
public class MyJavaBean{
private int id;
private Properties fItem; //Enumeration type
private String value;
private Rule rule; //Enumeration type
private int fId;
//GET, SET
#Override
public boolean equals(Object o){
if(!(o instanceof MyJavaBean))
return false;
MyJavaBeanv = (MyJavaBean) o;
return (fItem == null ? v.fItem == null : fItem.equals(v.fItem)) &&
(value == null ? v.value == null : value.equals(v.value)) &&
(rule == null ? v.rule == null : rule.equals(v.rule)) &&
fId == v.fId && id == v.id;
}
#Override
public int hashCode(){
int result = 17;
if(fItem != null)
result = 31 * result + fItem.hashCode();
if(value != null)
result = 31 * result + value.hashCode();
if(rule != null)
result = 31 * result + rule.hashCode();
result = 31 * result + fId;
result = 31 * result + id;
return result;
}
}
He also suggest we write unitTests to make sure the contracts are actually satisfied. Here is what he said(italic-bold emphasize mine):
When you are finished writing your equals method, ask yourself three
questions: Is it symmetric? Is it transitive? Is it consistent? And
don’t just ask yourself; write unit tests to check that these
properties hold!
So, I can't imagine how to write unit test for that pretty straightforward case. Well, I'd write something like that:
public class MyTest{
//Each pair consists of the equals object
private Map<MyJavaBean, MyJavaBean> equalValueMap;
#Before
public void init(){
//initializing the map, using ThredLocalRandom
//to get ints and enums randomly
}
#Test
public void testReflexive(){
for(MyJavaBean fiv: equalValueMap.keySet()){
Assert.assertEquals(fiv.equals(fiv), true);
}
}
#Test
public void testSymmetric(){
for(MyJavaBean fiv: equalValueMap.keySet()){
Assert.assertEquals(equalValueMap.get(fiv).equals(fiv), true);
Assert.assertEquals(fiv.equals(equalValueMap.get(fiv)), true);
}
}
#Test
public void testHashCode(){
for(FilterItemValue fiv: equalFilterValueMap.keySet()){
Assert.assertEquals(equalFilterValueMap.get(fiv).hashCode(), fiv.hashCode());
}
}
}
But I think such tests just waste the build-time, because of their simplicity. Is it worthy to write tests for the methods for simple JavaBeans?
IMHO! You can safely skip the tests for equals() and hashCode() if they are generated by an IDE / some helping library you trust.
Although, if you're really doing some complex things and worry if these methods may not behave as desired in runtime, it might be worth spending time and writing a simple test.
Related
Spring Expression Language (SpEL) in Spring Security to compare object use equals() or ==?
For example(method equals () is not called!):
class SecurityObject {
public boolean equals(Object obj) {
//...
}
}
#PreAuthorize(" #secObject == #otherSecObject ")
public void securityMethod(SecurityObject secObject, SecurityObject otherSecObject) {
//...
}
This is normal!? I need to use #PreAuthorize(" #secObject.equals(#otherSecObject) ") everywhere?
UPDATE
Why in first case Spring Security calling .equals(), and the second not?
//TestObject
public class TestObject {
private static final Logger log = LoggerFactory.getLogger(TestObject.class);
private Long id;
public TestObject(Long id) {
this.id = id;
}
#Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + Objects.hashCode(this.id);
return hash;
}
#Override
public boolean equals(Object obj) {
log.info("equals");
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TestObject other = (TestObject) obj;
if (!Objects.equals(this.id, other.id)) {
return false;
}
return true;
}
}
//TestService
#PreAuthorize(" #one == #two ")
public String testEqualsInAnnotation(Long one, Long two) {
//...
}
#Override
#PreAuthorize(" #one == #two ")
public String testEqualsInAnnotation(TestObject one, TestObject two) {
//...
}
//Test
log.info("for Long");
Long one = new Long(500);
Long two = new Long(500);
log.info("one == two: {}", (one==two)? true : false); // print false
log.info("one equals two: {}", (one.equals(two))? true : false); // print true
testService.testEqualsInAnnotation(one, two); //OK
log.info("for TestObject");
TestObject oneObj = new TestObject(new Long(500));
TestObject twoObj = new TestObject(new Long(500));
log.info("oneObj == twoObj: {}", (oneObj==twoObj)? true : false); // print false
log.info("oneObj equals twoObj: {}", (oneObj.equals(twoObj))? true : false); // print true
testService.testEqualsInAnnotation(oneObj, twoObj); // AccessDeniedException: Access is denied
UPDATE 2
equals() never invoked at all
package org.springframework.expression.spel.ast;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Implements equality operator.
*
* #author Andy Clement
* #since 3.0
*/
public class OpEQ extends Operator {
public OpEQ(int pos, SpelNodeImpl... operands) {
super("==", pos, operands);
}
#Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
} else {
return BooleanTypedValue.forValue(op1.intValue() == op2.intValue());
}
}
if (left!=null && (left instanceof Comparable)) {
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) == 0);
} else {
return BooleanTypedValue.forValue(left==right);
}
}
}
As per spEL documentation, You need to create ExpressionParser instance, create an Expression instance and get the value like below
String name = "Nikola Tesla";
Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(Boolean.class);
result evaluates to 'true'. That says when we need to compare any two objects, then we need to override the equals() method and pass the two objects in to parser#parseExpression("obj1 == obj2") and then call the exp#getValue(Boolean.class) to evaluate. In the similar way, the Expression instance can also have expression string containing Obj1.equals(Obj2) for checking the equality. so, both the ways of checking equality are possible with spEL.
You may have discovered this already, since it is in the OpEq code in 'Update 2' of the original post, but...
The comparison operators lt < gt > le <= ge >= eq == ne != are based on java's Comparable interface.
So, if you've got a custom type that you want to be able to compare using == or != in SpEL expressions, then you could write it to implement Comparable.
Of course, then you'll have to figure out some sane rule to decide which object is before the other when they're not equivalent.
That said, I can't find anything in Spring's current documentation indicating this.
rdm, I think you have to use permission evaluator to evaluate the expressions. I don't think you have really injected/passed values for the objects in the following expression.
#Override
#PreAuthorize(" #one == #two ")
public String testEqualsInAnnotation(TestObject one, TestObject two) {
//...
I tried to do the same thing, but I failed to pass values, hence couldn't able to evaluate the expressions. My suggestion is to implement your custom permission evaluator for the above expression, and inject/pass values from the evaluator. To generalize my idea, my suspect is the objects are null, that is why you couldn't able to evaluate it. Please let us know if you can really pass values of the objects inside here : #PreAuthorize(" #one == #two ")
Added:
I am using permission evaluator to evaluate expressions under #PreAuthorize(...) annotation. Because I couldn't able to pass values to the parameters as I explained above. If it is possible to pass/inject values, it will be good to reduce complexity that can come from using permission evaluator.
rdm or others, can you point me how to pass the values for the parameters under #PreAuthorize(...) if possible?
Sorry for asking another question on rdm's post, and thank you in advance for your help!.
Here are my requirements for unit testing:
I would like to unit test my production classes
I would like to separate test code and production code apart such that I can release production code only
This seems like reasonable requirements. However, a problem always arises when I need to use methods such as assertEquals on objects as these requires that the equals method is overridden. The equals method would have to be implemented in production classes but is actually only used for testing. This becomes even worse when good coding practices dictates that if equals is overridden, then should hashCode also be implemented resulting in even more unused production code that clutters the production classes.
Here is a simple example with a User model (IntelliJ autoimplemented equals and hashCode)
public class User
{
public long id;
public long companyId;
public String name;
public String email;
public long version;
#Override
public boolean equals(Object o)
{
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if(companyId != user.companyId) return false;
if(id != user.id) return false;
if(version != user.version) return false;
if(!email.equals(user.email)) return false;
if(!name.equals(user.name)) return false;
return true;
}
#Override
public int hashCode()
{
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (int) (companyId ^ (companyId >>> 32));
result = 31 * result + name.hashCode();
result = 31 * result + email.hashCode();
result = 31 * result + (int) (version ^ (version >>> 32));
return result;
}
}
As it can be seen, equals and hashCode takes up a lot of space and clutters the class.
One solution to the problem could be to create a class, UserTester, which could have an assertUserEquals method that could be used instead of eg. JUnit's assertEquals.
Another solution could be to create a UserComparator. However, it does not seem like JUnit have any assertEquals that takes a Comparator.
What is best practices on this point?
Uniutils has a perfect reflection equals method you can use for unit testing. This way your production code remains clear from all this test stuff.
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
Later in test:
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
If you're using Mockito it has it's own means to do the same thing:
Mockito.verify(userDeleter).delete(Mockito.refEq(user));
Not the most efficient but one possible way is to compare fields using reflection.
public class Foo {
int x;
Foo(int in){
x = in;
}
public static void main(String[] args) throws Exception{
Foo o1 = new Foo(1),o2= new Foo(1);
boolean allMatch = true;
Class<?> c = Class.forName("Foo");
Field[] fields = c.getDeclaredFields();
for(Field f: fields){
allMatch &= f.get(o1)==f.get(o2);
}
}
}
I see two different things:
Sometimes it's not desirable to override hashcode&equals. It can change behavior of your program and it can hurt your performance, see Java Overriding hashCode() method has any Performance issue?
If there is no customer requirement to override hashcode&equals, like it's value object, you would not do that. Your should provide such code which exactly meets customer criteria, not more. Your test should deal with original implementation in default object.
Problem Statement
I have two Collections of the same type of object that I want to compare. In this case, I want to compare them based on an attribute that does not factor into equals() for the Objects. In my example, I'm using ranked collections of Names for instance:
public class Name {
private String name;
private int weightedRank;
//getters & setters
#Override
public boolean equals(Object obj) {
return this.name.equals(obj.name); //Naive implementation just to show
//equals is based on the name field.
}
}
I want to compare the two Collections to assert that, for position i in each Collection, the weightedRank of each Name at that position is the same value. I did some Googling but didn't find a suitable method in Commons Collections or any other API so I came up with the following:
public <T> boolean comparatorEquals(Collection<T> col1, Collection<T> col2,
Comparator<T> c)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (c.compare(i1.next(), i2.next()) != 0) {
return false;
}
}
return true;
}
Question
Is there another way to do this? Did I miss an obvious method from Commons Collections?
Related
I also spotted this question on SO which is similar though in that case I'm thinking overriding equals() makes a little more sense.
Edit
Something very similar to this will be going into a release of Apache Commons Collections in the near future (at the time of this writing). See https://issues.apache.org/jira/browse/COLLECTIONS-446.
You could use the Guava Equivalence class in order to decouple the notions of "comparing" and "equivalence". You would still have to write your comparing method (AFAIK Guava does not have it) that accepts an Equivalence subclass instead of the Comparator, but at least your code would be less confusing, and you could compare your collections based on any equivalence criteria.
Using a collection of equivance-wrapped objects (see the wrap method in Equivalence) would be similar to the Adapter-based solution proposed by sharakan, but the equivalence implementation would be decoupled from the adapter implementation, allowing you to easily use multiple Equivalence criteria.
You can use new isEqualCollection method added to CollectionUtils since version 4. This method uses external comparsion mechanism provided by Equator interface implementation. Please, check this javadocs: CollectionUtils.isEqualCollection(...) and Equator.
I'm not sure this way is actually better, but it is "another way"...
Take your original two collections, and create new ones containing an Adapter for each base object. The Adapter should have .equals() and .hashCode() implemented as being based on Name.calculateWeightedRank(). Then you can use normal Collection equality to compare the collections of Adapters.
* Edit *
Using Eclipse's standard hashCode/equals generation for the Adapter. Your code would just call adaptCollection on each of your base collections, then List.equals() the two results.
public class Adapter {
public List<Adapter> adaptCollection(List<Name> names) {
List<Adapter> adapters = new ArrayList<Adapter>(names.size());
for (Name name : names) {
adapters.add(new Adapter(name));
}
return adapters;
}
private final int name;
public Adapter(Name name) {
this.name = name.getWeightedResult();
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + name;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Adapter other = (Adapter) obj;
if (name != other.name)
return false;
return true;
}
}
EDIT: Removed old answer.
Another option that you have is creating an interface called Weighted that could look like this:
public interface Weighted {
int getWeightedRank();
}
Then have your Name class implement this interface. Then you could change your method to look like this:
public <T extends Weighted> boolean weightedEquals(Collection<T> col1, Collection<T> col2)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (i1.next().getWeightedRank() != i2.next().getWeightedRank()) {
return false;
}
}
return true;
}
Then as you find additional classes that need to be weighted and compared you can put them in your collection and they could be compared with each other as well. Just an idea.
I've been using ArrayList for my project to store a cricket team players and order them.
I started thinking about using a TreeSet because of its advantage of removing duplicates.
However the problem I'm having is that if for example I create the following two players:
P p1 = new P("Jack","Daniel",33(age),180(height),78(weight),41(games played),2300
(runs scored),41(dismisses))
P p2 = new P("Jack","Daniel",37(age),185(height),79(weight),45(games played),2560
(runs scored),45(dismisses))
Notice that the two players have the same first and last name, but everything else is different. When I try to add these two players to the TreeSet, it considers them duplicates because of the names similarities and removes the second one. Obviously I don't want this to happen and I want the Set to remove a player only if everything he has is the same as another player, and not just the first and last names.
Is there a way of achieving this?
Also my TreeSet takes a Player object.
Originally, this answer neglected the fact that a TreeSet does its comparisons based on compareTo(), rather than equals(). Edits have been made to address this.
You need to define equals(), hashCode() and compareTo() for your Player object correctly. (Since it's a TreeSet and not a HashSet, implementing hashCode() isn't so important - but it's good practice.)
Equals and hashCode need to take into account all of the fields. Eclipse can auto-generate one for you that will look similar to this (Source > Generate hashcode and equals).
If you already have a natural sort order that doesn't use all of the fields, then you could supply a custom comparator to your TreeSet. However, even if you really only want to sort by a subset of the fields, there's nothing stopping you sorting by all fields (with the uninteresting fields only playing a part of the interesting parts are identical). The important thing to note here is that a TreeSet determines equality not by the equals() method, but by compareTo() == 0.
Here's an example equals():
#Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Player that = (Player) obj;
return this.age == that.age &&
this.height == that.height &&
this.weight == that.weight &&
this.games == that.games &&
this.runs == that.runs &&
this.dismisses == that.dismisses &&
this.given.equals(that.given) &&
this.family.equals(that.family);
}
And here's hashcode:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.age;
result = prime * result + this.dismisses;
result = prime * result + this.family.hashCode());
result = prime * result + this.games;
result = prime * result + this.given.hashCode());
result = prime * result + this.height;
result = prime * result + this.runs;
result = prime * result + this.weight;
return result;
}
Finally, here's a compareTo:
public int compareTo(Player that)
{
int result;
result = this.family.compareTo(that.family);
if (result != 0) // is the family name different?
{
return result; // yes ... use it to discriminate
}
result = this.given.compareTo(that.given);
if (result != 0) // is the given name different?
{
return result; // yes ... use it to discriminate
}
result = this.age - that.age; // is the age different?
if (result != 0)
{
return result; // yes ... use it to discriminate
}
... (and so on) ...
... with the final one ...
return this.dismisses - that.dismisses; // only thing left to discriminate by
}
a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.
From Java Platform Standard Edition 8 Documentation TreeSet part.
class Student implements Comparable<Student> {
String name;
public Student(String name) {
this.name=name;
}
public String toString(){
return name;
}
public int compareTo(Student gStudent) {
if(!this.name.equals(gStudent.getName()))
return 1;
return 0;
}
private String getName() {
return name;
}
}
List<Pair<String, String> > lp = new ArrayList<Pair<String, String> >();
lp.add(new Pair("1", "2"));
How should I check if the list lp contains 1 and 2 i.e the Pair ("1", "2").
Your Pair class needs to implement equals() and hashCode() and you're all set. List.contains() is implemented in terms of the type's equals() method. See the API for List.contains(). (Edited a bit to address comments from #maaartinus, whose answer you should read b/c the observations are solid, and it's a bit ridiculous for me to fold them in here. As maaartinus points out, a best-practice here would be to avoid error-prone manual definitions for equals and hashcode, and instead build on Guava's helper functions for nullable equals and hashCode for n objects).
final class Pair<T> {
final T left;
final T right;
public Pair(T left, T right)
{
if (left == null || right == null) {
throw new IllegalArgumentException("left and right must be non-null!");
}
this.left = left;
this.right = right;
}
public boolean equals(Object o)
{
// see #maaartinus answer
if (! (o instanceof Pair)) { return false; }
Pair p = (Pair)o;
return left.equals(p.left) && right.equals(p.right);
}
public int hashCode()
{
return 7 * left.hashCode() + 13 * right.hashCode();
}
}
With suitable equals(), you can now do:
lp.add(new Pair("1", "2"));
assert lp.contains(new Pair("1","2"));
Responding to the comments below, perhaps it would be good to include a good reference for "Why do I need to implement hashCode()?"
JavaPractices.com — Implementing equals() — "if you override equals, you must override hashCode"
Object.equals() contract as defined in the API documentation
StackOverflow answer
The implementation in the answer by andersoj
return left != null && right != null && left.equals(p.left) && right.equals(p.right);
is wrong: The null tests clearly suggest that null is a legal value for left and right. So there are at least two problems there:
new Pair(null, null).hashCode() throws NPE
new Pair(null, null) does NOT equal to itself!
Have a look at Guava class Objects for a correct implementation. Use it or write a static helper methods like
public static boolean equal(Object a, Object b) {
return a==b || a!=null && a.equals(b);
}
public static int hashCode(Object a) {
return a==null ? 0 : a.hashCode();
}
and always use them.
Never ever write equals containing a null test.
It's to easy to blow it, and nobody noticed it. Using the Helper, it's trivial to get it right:
public boolean equals(Object o) {
if (!(o instanceof Pair)) return false;
Pair p = (Pair) o;
return Helper.equals(left, p.left) && Helper.equals(right, p.right);
}
public int hashCode() {
return 7 * Helper.hashCode(left) + 13 * Helper.hashCode(right);
}
Of course, forbidding nulls in the constructor is an option, too.