I'ver been wondering how to best implement equals() for a family of classes that all implement the same interface (and the client is supposed to work only with said interface and never to know about implementing classes).
I haven't cooked up my own concrete example, but there are two examples in the JDK - java.lang.Number and java.lang.CharSequence that illustrate the decision:
boolean b1 = new Byte(0).equals( new Integer(0) ) );
or with CharSequence
boolean b2 = "".equals(new StringBuilder());
Would you ideally want those to evaluate to true or not? Both types do implement the same datatype interface, and as a client working with Numbers (resp. CharSequences) instances I would have an easier life if equals would compare the interface types instead of the implementing types.
Now this is not an ideal example, as the JDK exposes the implementing types to the public, but suppose we had not have to uphold compatibility with what is already there - from a designers point of view: Should equals check against the interface or is it better the way it is, checking against the implementation?
Note: I understand that checking for equality against an interface can be very hard to actually implement properly in practice and its made even more tricky since equal interfaces also need to return the same hashCode().
But those are only obstacles in implementation, take for example CharSequence, although the interface is pretty small, everything required for equality checks is present whithout revealing the internal structure of the implementation (so it is principally possible to implement properly, even without knowing about future implementations in advance).
But I am more interested in the design aspect, not on how to actually implement it. I wouldn't decide solely based on how hard something is to implement.
Define an abstract class that implements your interface and defines final equals()/hashCode() methods and have your customers extend that instead:
public interface Somethingable {
public void something();
}
public abstract class AbstractSomethingable implements Somethingable {
public final boolean equals(Object obj) {
// your consistent implementation
}
public final int hashCode() {
// your consistent implementation
}
}
Notice that by making your class abstract, you can implements the interface without defining the interface's methods.
Your customers still have to implement the something() method, but all their instances will use your code for equals()/hashCode() (because you've made those methods final).
The difference to your customers is:
Using the extends keyword instead of the implements keyword (minor)
Not being able to extend some other class of their choosing to use your API (could be minor, could be major - if it's acceptable then go for it)
I would normally assume that "similar" objects would not be equal - for example I wouldn't expect the Integer(1) would pass equals(Long(1)) . I can imagine situations where that would be reasonable, but as the jdk needs to be a general-purpose API you wouldn't be able to make the assumption that that would always be the correct thing to do.
If you've got some sort of custom objects where it's reasonable, I think it's perfectly fine to implement an expanded definition of equals if you
are sure that you don't have some edge cases where you really do need the more specific equality (i.e. that would require the identical classes)
document it very clearly
make sure that hashcode behaves consistently with your new equals.
For what it's worth, I'd probably do an implementation-specific equals implementation (side note - don't forget to implement hashCode...). Interface-level equals() puts a pretty heavy burden on implementers of the interface - who might or might not be aware of the special requirement.
Often, implementation-level works fine as your client only deals with one implementation (i.e. MyNumberProcessor can works on any Number, but practically one instance of it would only have to handle Long and maybe another only Double). Generics are a great way of making sure that happens.
In the rare case where it does matter, I would probably design the client to allow injection of a Comparator or - when not available - encapsulate my Numbers into a VarTypeNumber.
I'd try to add another equals Method to my interface. How about that:
assertFalse(new Integer(0).equals(new Byte(0))); // pass
assertTrue(new Integer(0).valueEquals(new Byte(0))); // hypothetical pass
This does not produce unexpected behaviour (different types equal) but keeps the possibility open to check for equal values.
There's a somewhat related topic in effective java where equals with instanceof and getClass is discussed. Can't remember the item number, though.
I would consider any implementation of equals that returns true for two objects that do not have the same concrete type to be extremely 'surprising' behavior. If you're operating inside a box where you know at compile time every possible implementor of the interface, you can fabricate equals that make sense with only interface methods, but that's not a reality for API/framework code.
You can't even be sure that nobody's going to write an implementation of the interface that mutates its internal state when you call the methods that you used to implement equals! Talk about confusing, an equals check that returns true and invalidates itself in the process?
--
This is what I understood to be the question as far as 'checking equality against the interface':
public interface Car {
int speedKMH();
String directionCardinal();
}
public class BoringCorrolla implements Car {
private int speed;
private String directionCardinal;
public int speedKMH() { return speed; }
public String directionCardinal() { return directionCardinal; }
#Override
public boolean equals(Object obj) {
if (obj isntanceof Car) {
Car other = (Car) obj;
return (other.speedKMH() == speedKMH() && other.directionCardinal().equals(directionCardinal());
}
}
}
public class CRAZYTAXI implements Car, RandomCar {
public int speedKMH() { return randomSpeed(); }
public String directionCardinal() { return randomDirection();}
}
It is possible to define equality among different classes.
In your case, the exact equality algorithm must be specified by the interface, so any class implementing the interface must abide by it. Better yet, since the algorithm depends only on information exposed by the inferface, just implement it already, so subclasses can simply borrow it.
interface Foo
class Util
static int hashCode(Foo foo){ ... }
static boolean equal(Foo a, Foo b){ ... }
static boolean equal(Foo a, Object b)
return (b instanceof Foo) && equal(a, (Foo)b);
class FooX implements Foo
int hashCode()
return Util.hashCode(this);
boolean equals(Object that)
return Util.equal(this, that);
Related
I am facing a bizarre outcome in a java application (Spring batch job) after one of the internal custom dependencies -a library we have developed in my company- has been upgraded.
After the upgrade in the code two new different objects of the same type show to have the same hash code.
CustomObject oj1 = new CustomObject();
oj1.setId(1234L);
CustomObject oj2 = new CustomObject();
oj2.setId(9999L);
System.out.println(oj1); //Prints CustomObject#1
System.out.println(oj2); //Prints CustomObject#1
System.out.println(oj1.hashCode()); //Prints 1
System.out.println(oj2.hashCode()); //Prints 1
I noticed this issue after realizing that one of the unit test which has a HashSet variable was only adding the very first object and ignoring the rests. Obviously the hashSet is doing what is supposed to do but the objects are not supposed to be the same and are new instances with different Ids. I tested the same thing outside of the unit test within the application and still the same issue. As soon as I revert back to the old dependency code behaves normally and the above print statements show different numbers!
I am sure one of the dependencies is causing this issue abut I am not able to determine the root cause.
CustomObject is being pulled indirectly through that same dependency and does not have equals() and hashcode() implemented, it only has
private static final long serialVersionUID = 1L;
Looking at the source of CustomObject reveals this implementation
public class CustomObject extends BaseModel implements Serializable
and BaseModel has the equals and hashCode methods defined
import org.jvnet.jaxb2_commons.lang.*;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "BaseModel")
#XmlSeeAlso({
CustomObject.class
})
public abstract class BaseModel implements Serializable, Equals2, HashCode2
{
private final static long serialVersionUID = 1L;
public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
if ((object == null)||(this.getClass()!= object.getClass())) {
return false;
}
if (this == object) {
return true;
}
return true;
}
public boolean equals(Object object) {
final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
return equals(null, null, object, strategy);
}
public int hashCode(ObjectLocator locator, HashCodeStrategy2 strategy) {
int currentHashCode = 1;
return currentHashCode;
}
public int hashCode() {
final HashCodeStrategy2 strategy = JAXBHashCodeStrategy.INSTANCE;
return this.hashCode(null, strategy);
}
}
Thank you in advance.
Clearly something has changed in a base class, and you will just have to find it and fix it, or else implement hashCode() and equals() acceptably in this class.
Somebody somewhere has implemented hashCode() to return 1, which is idiotic. They would have been better off not to implement it at all. And it's not hard to find. Just look at the Javadoc for CustomObject and see where it inherits hashCode() from.
I think you have already realized what the answer to your question is, but the code that you added in your update makes it clear:
Your CustomClass extends the BaseClass.
The BaseClass overrides Object::hashCode()
The override in the version of BaseClass that you showed us will always return 1. It is calling a hashCode(ObjectLocator, HashCodeStrategy2) method with a specific strategy, but the implementation of that method simply ignores the strategy argument.
Now is pretty clear that that version the BaseClass code can only ever return 1 as the hashcode. But you say that your code used to work, and you only changed the dependency. From that, we must conclude that the dependency has changed, and that the new version of the dependency is broken.
If anything is "bizarre" about this, it is that someone decided to implement the (new) BaseClass like that, and release it without testing it properly.
Actually, there is a possible way that you can get your CustomClass to work. The BaseClass::hashCode(ObjectLocator, HashCodeStrategy2) method is public and not final, so you could override it on your CustomClass as follows:.
#Override
public int hashCode(ObjectLocator locator, HashCodeStrategy2 strategy) {
return System.identityHashCode(this);
}
And indeed, it may be that the implementers of BaseClass intend you to do this. But I would still argue that BaseClass is broken:
Coding the class so that hashCode returns 1 as the default behavior is nasty.
There is no javadoc in BaseClass to explain the need to override the method.
Their (unannounced?) change to the behavior of BaseClass is an API breaking change. You shouldn't do stuff like that, without very good reason, and without warning.
The default behavior of the corresponding equals method is objectively wrong.
It's fine for two non-equal objects to have the same hash code. In fact, it's a mathematical requirement that this be allowed. Think about strings, for instance: there are infinitely many non-equal strings ("a", "aa", "aaa"...) yet only 2^32 possible int values. Clearly there must be different strings that share a hash code.
But HashSet knows this, so it uses the result from equals as well as the hash code. If only one of the objects is being added, then they don't just have there same hash code -- they are equal, as returned by the equals method. We can't determine why this is, let alone whether it's intentional, without the custom class's code.
The contract for Object says that equal objects must have the same hash code. But the converse is not true: objects with the same hash code do not have to be equal. The Javadocs say this explicitly:
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
Unless a class's docs explicitly tell you how it computes its hash code, you should probably not consider that an established contact, and you should expect that it could change between versions.
CustomObject class implementation (or one of its ancestors) is the problem here. Author of CustomObject (or one of its ancestors) has overridden toString, hashCode and equals methods in a wrong way without understanding the semantics of it and the implications of it. Here are your options (not necessarily in that order) to fix the problem:
You should notify the author of your dependency library about the problem in CustomObject class and get toString, hashCode and equals methods implemented or overridden in the right way. But remember - dependency code author can put you back in this place again in future.
Assuming CustomObject class is not final - extend CustomObject (please note - it is better named as CustomClass, not CustomObject) to have right implementation of toString, hashCode and equals methods. Use this extended class in your code instead of CustomObject class. This will give you better control because your dependency code cannot put you in this trouble again.
Use AOP to introduce overridden and right implementation of toString, hashCode and equals methods in CustomObject class. This approach is also future proof like the option 2 above.
I'm trying to write some generic code to define class equality and hashcodes based on a list of fields. When writing my equals method, I was wondering if, based on Java convention, it should ever be possible for two object of different to be equal. Let me give some examples;
class A {
int foo;
}
class B {
int foo;
}
class C extends A {
int bar;
}
class D extends A {
void doStuff() { }
}
...
A a = new A(); a.foo = 1;
B b = new B(); b.foo = 1;
C c = new C(); c.foo = 1; c.bar = 2;
D d = new D(); d.foo = 1;
a.equals(b); //Should return false, obviously
a.equals(c);
c.equals(a); //These two must be the same result, so I'd assume it must be false, since c cant possible equal a
a.equals(d); //Now this one is where I'm stuck.
I see no reason that in the last example the two shouldn't be equal, but they do have different classes. Anyone know what convention dictates? And if they would be equal, how should an equals method handle that?
Edit: if anyone's interested in the code behind this question, see: https://gist.github.com/thomaswp/5816085 It's a little dirty but I'd welcome comments on the gist.
They could be, but it's typically very difficult to maintain the symmetric and transitive properties of equality in that case. At least while having a useful/intuitive definition of equality.
If you allow a subclass to consider itself equal to an instance of the superclass, then the superclass needs to consider itself equal to an instance of the subclass. Which means that you'll be encoding specific knowledge about the subclass (all possible subclasses?) in the superclass, and downcasting as needed, which isn't very clean.
Or, you do the comparison purely with fields contained in A, and don't override equals() at all. This fixes the above, but has the problem that two instances of C with different values of bar would be considered equal, which is probably not what you want.
Or, you override in C, and compare bar if the other object is an instance of C, but otherwise don't for an instance of A, you have another problem. c1.equals(c2) would be false, but c1.equals(a) would be true, as would c2.equals(a) and so a.equals(c2). This breaks transitivity (since c1 == a and a == c2 implies c1 == c2).
In summary, it's theoretically possible but you would have to cripple your equals implementation to do so. And besides, the runtime class is a property of an object just as much as bar is, so I'd expect objects with different concrete classes to be not equal to each other anyway.
First note: when you override .equals(), you absolutely MUST override .hashCode() as well, and obey the defined contract. This is no joke. If you do not obey THAT, you are doomed to encounter problems.
As to handling equality between different classes inheriting one another, if all these classes have a common member, this can be done as such:
#Override
public int hashCode()
{
return commonMember.hashCode();
}
#Override
public boolean equals(final Object o)
{
if (o == null)
return false;
if (this == o)
return true;
if (!(o instanceof BaseClass))
return false;
final BaseClass other = (BaseClass) o;
return commonMember.equals(other.commonMember); // etc -- to be completed
}
Also remember that you need to follow these rules in order to correctly implement the equals method.
Reflexive : Object must be equal to itself.
Symmetric : if a.equals(b) is true then b.equals(a) must be true.
Transitive : if a.equals(b) is true and b.equals(c) is true then c.equals(a) must be true.
Consistent : multiple invocation of equals() method must result same value until any of properties are modified. So if two objects are equals in Java they will remain equals until any of there property is modified.
Null comparison : comparing any object to null must be false and should not result in NullPointerException. For example a.equals(null) must be false, passing unknown object, which could be null, to equals in Java is is actually a Java coding best practice to avoid NullPointerException in Java.
As Andrzej Doyle rightly said, it becomes difficult to implement the Symetric and Transitive property when it's spread across multiple classes.
Object.equals() is required to be reflexive, symmetric, transitive, consistent across multiple invocations, and x.equals(null) must be false. There are no further requirements beyond that.
If equals() for a class you define does all of those things, then it's an acceptable equals() method. There is no answer to the question of how fine-grained it should be other than the one you provide yourself. You need to ask yourself: Which objects to I want to be equal?
Note, however, that you should have a good reason for making a.equals(b) true when a and b are instances of different classes, as that can make it tricky to implement a correct equals() in both classes.
This question seems to me to indicate a muddy architecture. In theory, if you want to implement .equals such that you compare only specific members of the two instances you can do this, but whether this is a good idea really depends on just what purpose these classes are intended to serve (and even then I think there are better approaches).
Are these objects, more or less, just intended to be nothing more than bags of data? If so, perhaps you should create a separate comparison class that determines whether the two objects are "equivalent enough" for the purposes you need, rather than force the objects themselves to care about some alien, unrelated class. I'd be concerned if my code were concerning itself with potentially unrelated objects just because I thought it might be a good idea for them to know about each other due to temporary convenience. Also, as Andrzej mentioned, it's very problematic for a parent class to know or care about specific implementation details of derived classes. I've seen first-hand how this causes problems both subtle and egregious.
Are the objects "doers" rather than data storage? Since your subclass D implements a method then this indicates that it's more than just a bag of data... and in this case, philosophically, I can't see how it would be a good idea to consider A and D equal based merely on a set of value fields. Compare the fields, yes. Consider them equal or equivalent? No. This sounds like a maintainability nightmare in the long haul.
Here's an example of what I think would be a better idea:
class A implements IFoo{
private int foo;
public int getFoo(){ return foo; }
}
class B implements IFoo{
private int foo;
public int getFoo(){ return foo; }
}
class CompareFoos{
public static boolean isEquivalent(IFoo a, IFoo b){
// compare here as needed and return result.
}
}
IFoo a = new A();
IFoo b = new B();
boolean result = CompareFoos.isEquivalent(a, b);
To some extent it depends on what you want the code to do. As long as you are clear what the equals method will compare then I don't see a problem necessarily. I think the problem really comes when you start making lots of sub-classes (e.g. Class E). There's a danger than that one of the sub-classes won't follow the contract so you could end up with
a.equals(e) --> true
e.equals(a) --> false
which would result in strange behaviour.
Personally I try to avoid equals comparing two different classes and returning true but I have done it a couple of times where the whole class hierarchy was under my control and final.
Consider this example -
abstract class Quadrilateral{
Quadrilateral(int l, int w){
length=l;
width=w;
}
private int length, width;
}
class Rectangle extends Quadrilateral{
Rectangle(int l, int w){super(l,w);}
}
class Square extends Quadrilateral{
Square(int l){super(l, l);}
}
Square s = new Square(3);
Rectangle r = new Rectangle(3,3);
r.equals(s);//This should be true because the rectangle and square are logically the same.
So yes, there are cases where two different classes can be equal. Though clearly this is not common.
No.
Set<Parent> p = new HashSet<>();
p.insert(new Parent(1));
p.insert(new Child(1));
Supposing those two instances are equals, does p contain a Child? Very unclear. More fun:
class Parent {
public int foo = 0;
void foo() {
foo = 1;
}
}
class Child extends Parent {
#Override
void foo() {
foo = 2;
}
}
Set<Parent> set = new HashSet<>();
for(Parent parent : set) {
parent.foo();
System.out.println(parent.foo); // 1 or 2?
}
I challenge you to know what p's element contains without spending more than 1 minute on the Javadoc pages for Set, HashSet, or equals.
The short answer is that in very limited cases (e.g. Integer and Long) it is OK for two objects of different classes to be equal, but it is so hard to pull off correctly that it is generally discouraged. It's hard enough to ensure that you create an equals() method that is Symmetric, Reflexive, and Transitive, but on top of that you should also:
create a hashCode() method that is consistent with equals()
create a compareTo() method that is consistent with equals()
further ensure that the compareTo() method is well behaved
Failure to do that can cause problems with using the objects in Sets, Trees, Hashes, and Sorted Lists.
There's a good article on the the topic of equals() based on part of Josh Bloch's book Effective Java but even that only covers equals(). The book goes into much greater detail, especially about how the problem becomes serious quickly once you start using the objects in collections.
It depends what you need, as #andrzeg said.
One additional thing. Do you want a.equals(d) to be true, but d.equals(a) to be false? when coding you will probably be using instanceof which may or may not be what you want.
I think that this question can be reduced to whether to use instanceof or getClass() in equals() implementation.
If D derives A and your inheritance hierarchy is right then indeed D IS A. It is logical to treat D as any other A and therefore you should be able to check D for equality as you would any other A.
Here is a link where Josh Bloch explains why he favors the instanceof approach.
http://www.artima.com/intv/bloch17.html
I have a problem with overriding the equals method in an Enum to make it compatible with other classes.
The Enum implements an interface and the idea is that all implementations of this interface can be tested for equality, regardless of their type. For Example:
public interface Group {
public Point[] getCoordinates();
}
public enum BasicGroups implements Group {
a,b,c; // simplified, they actually have constructors
// + fields and methods
}
public class OtherGroup implements Group {
// fields and methods
}
If both a BasicGroup and an OtherGroup have the same coordinates (in arbitrary order) then the equals method should return true.
No problem when performing myOtherGroup.equals(BasicGroup.a) but since the equals method in Enums is final, I can't override them.
Is there some way to work around this? Like when testing on another BasicGroup the default equals method (reference equality) is used and when testing other classes my own implementation is used. And how do I make sure that java doesn't use the wrong one when I do BasicGroup.a.equals(myOtherGroup)?
You can NOT #Override a final method (§8.4.3.3); this much is clear. enum types (§8.9) are treated very specially in Java, which is why the equals is final (also clone, hashCode, etc.) It's simply not possible to #Override the equals method of an enum, nor would you really want to in a more typical usage scenario.
HOWEVER, looking at the big picture, it looks like you are trying to follow the pattern recommended in Effective Java 2nd Edition, Item 34: Emulate extensible enums with interfaces (see the language guide for more information about enum):
You have defined this interface (now documented explicitly for expected equals behavior):
public interface Group implements Group {
public Point[] getCoordinates();
/*
* Compares the specified object with this Group for equality. Returns true
* if and only if the specified object is also a Group with exactly the same
* coordinates
*/
#Override public boolean equals(Object o);
}
It is perfectly acceptable for an interface to define how equals method for implementors should behave, of course. This is exactly the case with, e.g. List.equals. An empty LinkedList is equals to an empty ArrayList and vice versa, because that's what the interface mandates.
In your case, you've chosen to implement some Group as enum. Unfortunately you now can't implement equals as per the specification, since it's final and you can't #Override it. However, since the objective is to comply to the Group type, you can use decorator pattern by having a ForwardingGroup as follows:
public class ForwardingGroup implements Group {
final Group delegate;
public ForwardingGroup(Group delegate) { this.delegate = delegate; }
#Override public Point[] getCoordinates() {
return delegate.getCoordinates();
}
#Override public boolean equals(Object o) {
return ....; // insert your equals logic here!
}
}
Now, instead of using your enum constants directly as Group, you wrap them in an instance of a ForwardingGroup. Now this Group object will have the desired equals behavior, as specified by the interface.
That is, instead of:
// before: using enum directly, equals doesn't behave as expected
Group g = BasicGroup.A;
You now have something like:
// after: using decorated enum constants for proper equals behavior
Group g = new ForwardingGroup(BasicGroup.A);
Additional notes
The fact that enum BasicGroups implements Group, even though it does not itself follow the specification of Group.equals, should be very clearly documented. Users must be warned that constants must be e.g. wrapped inside a ForwardingGroup for proper equals behavior.
Note also that you can cache instances of ForwardingGroup, one for each enum constants. This will help reduce the number of objects created. As per Effective Java 2nd Edition, Item 1: Consider static factory methods instead of constructors, you may consider having ForwardingGroup define a static getInstance(Group g) method instead of a constructor, allowing it to return cached instances.
I'm assuming that Group is an immutable type (Effective Java 2nd Edition, Item 15: Minimize mutability), or else you probably shouldn't implement it with enum in the first place. Given that, consider Effective Java 2nd Edition, Item 25: Prefer lists to arrays. You may choose to have getCoordinates() return a List<Point> instead of Point[]. You can use Collections.unmodifiableList (another decorator!), which will make the returned List immutable. By contrast, since arrays are mutable, you'd be forced to perform defensive copying when returning a Point[].
See also
Using the decorator design pattern for a hierarchy of classes
when do we need Decorator Pattern?
com.google.common.collect.ForwardingObject
What is the best way to cache and reuse immutable singleton objects in Java?
It's not possible to do this in Java. (The sole purpose of the final keyword when it comes to methods, is to prevent overriding!)
equals and a few other methods on Enums are final, so you can't change the behavior of them. (And you shouldn't :) Here is my answer to a related question:
The intuition of clients that deal with enum constants is that two constants are equal if and only if they are the same constant. Thus any other implementation than return this == other would be counterintuitive and error prone.
Same reasoning applies to hashCode(), clone(), compareTo(Object), name(), ordinal(), and getDeclaringClass().
The JLS does not motivate the choice of making it final, but mentions equals in the context of enums here. Snippet:
The equals method in Enum is a final method that merely invokes super.equals on its argument and returns the result, thus performing an identity comparison.
You can solve this by calling your method hasSameCoordinatesAs, or similar, rather than equals.
equals for enums is defined in the language specification, so you can't hope to redefine it.
Equality is quite elusive. Different contexts require different equality relations. By having equals() method on Object, Java imposes an "intrinsic" equality, and APIs, like Set, depend on it.
Meanwhile, ordering isn't considered "intrinsic", two objects can be ordered differently in different contexts, and APIs usually allow us to supply a comprator, i.e., a custom ordering relation.
This is interesting. In math terms, equality, like order, is just a relation, and there can be different equality relations. The concept of "intrinsic equality" isn't holy.
so let's have an Equal-ator too, and change APIs to accept custom equality relations:
interface Equalator
boolean equal(a, b)
public HashSet( Equalator equalator )
Actually, we can build wrappers around current collection APIs, and add this feature of new equality.
This might answer your question. Why do you have a dependency on equals() in the first place? And can you remove that, and depend instead on "equalator"? Then you are set.
I have an interface and two objects implementing that interface, massively simplied;
public interface MyInterface {
public int getId();
public int getName();
...
}
public class A implements MyInterface {
...
}
public class B implements MyInterface {
...
}
We are migrating from using one implementation to the other but I need to check that the objects of type B that are generated are equivalent to those of type A. Specifically I mean that for all of the interface methods an object of Type A and Type B will return the same value (I'm just checking my code for generating this objects is correct).
How would you go about this?
Map<String, MyInterface> oldGeneratedObjects = getOldGeneratedObjects();
Map<String, MyInterface> newGeneratedObjects = getNewGeneratedObjects();
// TODO: Establish that for each Key the Values in the two maps return equivalent values.
I'm looking for good coding practices and style here. I appreciate that I could just iterate through one key set pulling out both objects which should be equivalent and then just call all the methods and compare, I'm just thinking there may be a cleaner, more extensible way and I'm interested to learn what options there might be.
Would it be appropriate / possible / advised to override equals or implement Comparable?
Thanks in advance,
Gavin
I would implement a custom version of equals in the test class, not inside any of those implementation classes (since it would clash with the regular equals contract). Something like:
boolean equals(A a, B b) ...
I understand that this check would be required only during the migration period, so the normal equals methods of each implementation should not be affected by this. Namely, A.equals should only return true for an equal instance of A, and should always return false for an instance of B. And vice versa.
Once the migration is over, you no longer need class A neither the tester class, and you can continue using class B without needing to touch its implementation.
Note that if MyInterface (or A and B) extends Comparable, you should also test that the implementations in A and B are equivalent.
(And you surely know that if you implement equals you must also implement hashCode.)
This is a general question on how to unit-test a Java Class using mock objects.
I can summarize my problem with this example. Let's say I've an Interface called MyInterface.java and a "TwoString" Object that doesn't override equals()
"TwoString.java"
private String string1;
private String string2;
public TwoString(String string1, String string2) {
this.string1 = string1;
this.string2 = string2;
}
...getters..setters..
"MyInterface.java"
void callMe(TwoString twoString);
Then I have "MyClass.java" Object. Its constructor accepts a concrete implementation of MyInterface.
MyClass methodToTest() contains the logic to create a TwoString oject in some way. Let's say that it will be created as
new TwoString("a","b")
So when methodToTest() is called it creates this TwoString object that will be passed to the Interface method callMe(TwoString twoString).
I basically want to mock the Interface. Create a MyClass object with this mock. Then verify that the mock method is called with a specific instance of TwoString.
I'm using EasyMock and this is some java code
"MyClassTest.java"
public void test() throws Exception {
MyInterface myInterfaceMock = createMock(MyInterface.class);
MyClass myClass = new MyClass(myInterfaceMock);
myInterfaceMock.callMe(new TwoString("a","b")); <--- fails here
expectLastCall();
replay(myInterfaceMock);
myClass.methodToTest();
verify(myInterfaceMock);
Here comes the problem. The TwoString object that I'm expecting in the call
myInterfaceMock.callMe(new TwoString("a","b"));
is different from the one generated in MyClass.methodToTest() because TwoString.java doesn't override equals.
I can skip the problem on the TwoString instance using
myInterfaceMock.callMe((TwoString)anyObject());
but I want to be sure that the interface method is called with a specific instance of TwoString that contains "a" as string1 and "b" as string2.
In this case the TwoString object is very simple and it will be easy to override the equals method - but what if I need to check a more complex object.
Thanks
edit:
I'll try to make it more readable with this example
public class MyClassTest {
private MyClass myClass;
private TaskExecutor taskExecutorMock;
#Before
public void setUp() throws Exception {
taskExecutorMock = createMock(TaskExecutor.class);
myClass = new MyClass(taskExecutorMock);
}
#Test
public void testRun() throws Exception {
List<MyObj> myObjList = new ArrayList<MyObj>();
myObjList.add(new MyObj("abc", referenceToSomethingElse));
taskExecutorMock.execute(new SomeTask(referenceToSomethingElse, ???new SomeObj("abc", referenceToSomethingElse, "whatever"))); <--- ??? = object created using data in myObjList
expectLastCall();
replay(taskExecutorMock);
myClass.run(myObjList);
verify(taskExecutorMock);
}
}
???SomeObj = object created by myClass.run() using data contained in myObjList.
Let's say that SomeObj comes from a third party library and it doesn't override equals.
I want to be sure that the taskExecutorMock.execute() method is getting called with a specific instance of that SomeObj
How can I test that the myClass.run() is actually calling the taskExecutorMock method with a correct instance
It is possible to use a custom equals matching method using org.easymock.IArgumentMatcher.
It should look something like:
private static <T extends TwoString> T eqTwoString(final TwoString twoString) {
reportMatcher(new IArgumentMatcher() {
/** Required to get nice output */
public void appendTo(StringBuffer buffer) {
buffer.append("eqTwoString(" + twoString.getString1() + "," + twoString.getString2() + ")");
}
/** Implement equals basically */
public boolean matches(Object object) {
if (object instanceof TwoString) {
TwoString other = (TwoString) object;
// Consider adding null checks below
return twoString.getString1().equals(object.getString1()) && twoString.getString2().equals(object.getString2());
}
else {
return false;
}
}
});
return null;
}
And is used as follows:
myInterfaceMock.callMe(eqTwoString(new TwoString("a","b")));
Some details may not be correct, but in essence it's how I've done it before. There is another example and more thorough explanations available in the EasyMock documentation. Just search for IArgumentMatcher.
First up - you probably mean "override equals" rather than implement, since all classes have some implementation of equals (the one they inherit from Object if they don't override anything else).
The answer in this case is simple - all value objects really really ought to implements equals and hashcode. Whether it's a simple one like TwoString, or the more complex object you allude to, it should be the object's responsibility to check whether it is equal to some other object.
The only other alternative would be to basically deconstruct the object in your test code - so instead of
assertEquals(expected, twoStr);
you'd do
assertEquals(expected.getStringOne(), twoStr.getStringOne());
assertEquals(expected.getStringTwo(), twoStr.getStringTwo());
Hopefully you can see that this is bad in at least three ways. Firstly, you're basically duplicating the logic that should be in the class' own equals() method; and anywhere that you want to compare these objects you'll have to write the same code.
Secondly, you can only see the object's public state, there could well be some private state that causes two apparently similar objects to be not equal (e.g. a Lift class could have a publically accessible "floor" attribute, but private "going up or down" state too).
Finally, it's a violation of the Law of Demeter for a third-party class to be basically messing about with the internals of TwoString trying to work out whether the things are equal.
The object itself should implement its own equals() method - pure and simple.
Take a look at Jakarta Commons Lang: EqualsBuilder.reflectionEquals()
While I agree with dtsazza that all value objects should have an equals() (and hashCode()) method, they're not always appropriate to testing: most value objects will base equality on a key, rather than on all fields.
At the same time, I'm leery of any test that wants to check all fields: it seems to me to be saying "make sure this method didn't change something that I wasn't planning for it to change." Which is a valid test, and on some level a very well-meaning test, but it's a little scary that you feel the need for it.
In this case the TwoString object is very simple and it will be easy to override the equals method - but what if I need to check a more complex object.
Once your objects start becoming so complex that you can't trivially check if they're equal from elsewhere, you should probably refactor and inject them as a dependency. This would change the design, but usually that's for the better.
You also seem to be relying on some knowledge of the internal behaviour of your classes. The above is an interaction test between two classes, which still sort of works, but the bigger your set of tested components gets, the less you can really still talk about "unit" tests. At a certain point you leave the realm of unit tests and you start doing integration tests, in which case you might be better off with a full blown test harness and isolating behaviour in certain places...
You can achieve this with argument captors in Mockito 1.8.
http://mockito.googlecode.com/svn/branches/1.8.0/javadoc/org/mockito/Mockito.html#15
I know you are using EasyMock but changing to Mockito is easy and it's much better!