How can I check two Object-Arrays for Equality in JUnit? - java

I have a JAVA class NoName whose objects have the method getProperties(). This method returns an Array of Property.
When I now have two instances of NoName, how can I use assertEquals to check whether both instances' Property-Arrays are the same?
_assertEquals(inst.getProperties(), ance.getProrties())_ won't do the job, because it's deprecated.
And since the NoName class is a library class I cannot overwrite equals() (which seems to be the usual solution for this kind of problem, as far as I read until now).
Thanks in advance.

assertThat(ob1.getProperties(),
IsArrayContainingInOrder.contains(obj2.getProperties));
This is using a Hamcrest Matcher which I believe to be a preferable method to doing asserts since the output on failure is much more descriptive.
There is also an IsArrayContainingInAnyOrder if order does not matter.
IsArrayContainingInAnyOrder

Since you're talking about comparing an internal structure of an object, you can either override the equals method for the NoName class, and inside that compare the array of properties for both objects. But then, you'll need to take care of the hashCode method too.
Or, you can simply create a helper method hasSameProperties(NoName obj) in the NoName class, and make that method return a boolean flag after comparing the property arrays of both the objects. Then in JUnit, you can simply use the assertTrue method.

Property[] one = inst.getProperties(), two = ance.getProperties();
assertEquals(one.length, two.length);
for (int i=0; i<one.length; i++) assertEquals("index #" + i, one[i], two[i]);
That provides a basic scrub. If you're worried about some other edge conditions (e.g., like the array might be null, or that the array is allowed to have a different order as long as all the same elements are in each), then some extra work will be required (e.g., null check, a sort if Property implements Comparable).

Hamcrest 1.3 uses a slightly different syntax, some examples:
import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining;
...
#Test
public void inOrder1() {
assertThat(new String[]{"foo", "bar"}, arrayContaining(equalTo("foo"), equalTo("bar")));
}
#Test(expected = AssertionError.class)
public void inOrder2() {
assertThat(new String[]{"bar", "foo"}, arrayContaining(equalTo("foo"), equalTo("bar")));
// Expected: ["foo", "bar"]
// but: item 0: was "bar"
}
#Test(expected = AssertionError.class)
public void inOrder3() {
assertThat(new String[]{"foo", "bar", "lux"}, arrayContaining(equalTo("foo"), equalTo("bar")));
// Expected: ["foo", "bar"] in any order
// but: Not matched: "lux"
}
#Test(expected = AssertionError.class)
public void inOrder4() {
assertThat(new String[]{"foo", "bar"}, arrayContaining(equalTo("foo"), equalTo("bar"), equalTo("lux")));
// Expected: ["foo", "bar", "lux"] in any order
// but: No item matched: "lux" in ["foo", "bar"]
}

Related

HashSet equality called with passed object instead of stored item

In the following code, the output shows CONTAINS for each object, whereas commenting out the anonymous object's equals() method results in MISSING, which leads me to believe the second equality pass (hashCode() -> equals()) actually calls the equality method of the supplied object instead of the object within the collection being tested.
List<String> strings = Arrays.asList("Hello", "there", "Qix");
HashSet<String> set = new HashSet<>(strings);
for(final String s : strings)
{
boolean contains = set.contains(new Object(){
#Override
public int hashCode() {
return s.hashCode();
}
#Override
public boolean equals(Object obj) {
return true;
}
});
System.out.format("%s: %s\n",
s,
contains ? "CONTAINS" : "MISSING");
}
Why is this? Is it because the equals() method, by principle, should be symmetric between the two objects?
The HashSet either has to do a.equals(b) or b.equals(a). And as they should be written to be symmetric*, it shouldn't matter which it chooses.
But for reference, the documentation states:
returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e))
* See http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object).
It's an implementation detail which we shouldnt worry about since it's never said in public API how it uses equals. Like you said it's supposed to be symmetric anyway. If we go into src we'll see that it is really passedObject.equals(storedObject)
Becaue AFAIK the default Object.equals implementation uses reference comparison so it checks if the two references refer to the same object.
Here you are comparing instances of Strings with another custom classe, they can't be equal, whatever the way this is done.

Creating compareTo method in java with one parameter

I have a question about making a compareTo function in Java.
In Java, we have the String.compareTo(String) method.
However, I need to make a compareTo function with only only parameter, like: compareTo(String).
I assume that I need to use this to hold another string.
For example:
public static boolean compareTo(String word)
{
private string this.word = word;
if(word.equals(this.word))
{
return true;
}
else
{
return false;
}
}
Is this the right idea?
Do I need to create get and set functions to hold first word to compare with second word?
Thanks
To compare two objects, you need to implement the Comparable interface. As part of the implementation, you will write your own compareTo() method. This method compares your current object with the object being passed.
public MyObj implements Comparable<MyObj> {
...
public int compareTo(MyObj anObj) {
// if your obj greater than anObj, return 1
// if equal, return 0
// else return -1
}
}
Further down in your code, you can then do --
`MyObj anObj = new MyObj();
MyObj anObj1 = new MyObj();
// anObj.compareTo(anObj1) ....
// This will also be useful if you have a collection of MyObjs.
Collections.sort(arrayListOfMyObjs);
That's not the right idea in many ways...
You cannot use this in a static function.
You cannot add a visibility declaration to a local variable of a function.
There is no string but String in Java.
You make this.word equals to word then check if they are equal...
You don't need to do if/else to return a boolean: just do return x.equals(y); (not necessarily wrong, but that's a personal pet peeve...).
compareTo, the classical one, isn't equals, but returns -1, 0 or 1 depending if one object is lower, equals or higher than the other.
Revise your lessons... :-)
In your code, the method compareTo is static, so you can not use "this."
I suggest you'd better NOT make compareTo method static.

Java using contains function to match string object ignore capital case?

I want that the contain function should return true even if the following are in capital letters
List<String> pformats= Arrays.asList("odt","ott","oth","odm","sxw","stw","sxg","doc","dot","xml","docx","docm","dotx","dotm","doc","wpd","wps","rtf","txt","csv","sdw","sgl","vor","uot","uof","jtd","jtt","hwp","602","pdb","psw","ods","ots","sxc","stc","xls","xlw","xlt","xlsx","xlsm","xltx","xltm","xlsb","wk1","wks","123","dif","sdc","vor","dbf","slk","uos","pxl","wb2","odp","odg","otp","sxi","sti","ppt","pps","pot","pptx","pptm","potx","potm","sda","sdd","sdp","vor","uop","cgm","bmp","dxf","emf","eps","met","pbm","pct","pcd","pcx","pgm","plt","ppm","psd","ras","sda","sdd","sgf","sgv","svm","tgs","tif","tiff","vor","wmf","xbm","xpm","jpg","jpeg","gif","png","pdf","log");
if(pformats.contains(extension)){
// do stuff
}
A Set is a better choice for a lookup.
private static final Set<String> P_FORMATS = new HashSet<String>(Arrays.asList(
"odt,ott,oth,odm,sxw,stw,sxg,doc,dot,xml,docx,docm,dotx,dotm,doc,wpd,wps,rtf,txt,csv,sdw,sgl,vor,uot,uof,jtd,jtt,hwp,602,pdb,psw,ods,ots,sxc,stc,xls,xlw,xlt,xlsx,xlsm,xltx,xltm,xlsb,wk1,wks,123,dif,sdc,vor,dbf,slk,uos,pxl,wb2,odp,odg,otp,sxi,sti,ppt,pps,pot,pptx,pptm,potx,potm,sda,sdd,sdp,vor,uop,cgm,bmp,dxf,emf,eps,met,pbm,pct,pcd,pcx,pgm,plt,ppm,psd,ras,sda,sdd,sgf,sgv,svm,tgs,tif,tiff,vor,wmf,xbm,xpm,jpg,jpeg,gif,png,pdf,log".split(","));
if(P_FORMATS.contains(extension.toLowerCase())){
// do stuff
}
Short answer: Will not work. You can't overwrite the contains, BUT: You can us the following code:
List<String> pformats= Arrays.asList("odt","ott","oth","odm","sxw","stw","sxg","doc","dot","xml","docx","docm","dotx","dotm","doc","wpd","wps","rtf","txt","csv","sdw","sgl","vor","uot","uof","jtd","jtt","hwp","602","pdb","psw","ods","ots","sxc","stc","xls","xlw","xlt","xlsx","xlsm","xltx","xltm","xlsb","wk1","wks","123","dif","sdc","vor","dbf","slk","uos","pxl","wb2","odp","odg","otp","sxi","sti","ppt","pps","pot","pptx","pptm","potx","potm","sda","sdd","sdp","vor","uop","cgm","bmp","dxf","emf","eps","met","pbm","pct","pcd","pcx","pgm","plt","ppm","psd","ras","sda","sdd","sgf","sgv","svm","tgs","tif","tiff","vor","wmf","xbm","xpm","jpg","jpeg","gif","png","pdf","log");
if(pformats.contains(extension.toLowerCase())){
}
This will make you extension to lowercase, and if within your Array are all extensions are already lowerCase, than it'll wokk.
Convert your List of extensions into a regular expression, compile it with the CASE_INSENSITVE flag, and use that.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class Foo {
public static void main(final String... args) {
final Pattern p = Pattern.compile("odt|ott|oth|odm|sxw|stw|sxg|doc|dot|xml|docx|docm|dotx|dotm|doc|wpd|wps|rtf|txt|csv|sdw|sgl|vor|uot|uof|jtd|jtt|hwp|602|pdb|psw|ods|ots|sxc|stc|xls|xlw|xlt|xlsx|xlsm|xltx|xltm|xlsb|wk1|wks|123|dif|sdc|vor|dbf|slk|uos|pxl|wb2|odp|odg|otp|sxi|sti|ppt|pps|pot|pptx|pptm|potx|potm|sda|sdd|sdp|vor|uop|cgm|bmp|dxf|emf|eps|met|pbm|pct|pcd|pcx|pgm|plt|ppm|psd|ras|sda|sdd|sgf|sgv|svm|tgs|tif|tiff|vor|wmf|xbm|xpm|jpg|jpeg|gif|png|pdf|log", Pattern.CASE_INSENSITIVE);
// Will be true
System.out.println(p.matcher("bmp").matches());
// Will be false
System.out.println(p.matcher("quasar").matches());
}
}
This would probably be easier to read/maintain if you build the regex programatically, but I've left that as an exercise to the reader.
How about:
extension.toLowerCase()
?
Although I'm not sure 100% sure what contains() method will do in this example. You might need to stick your extensions into a Set.
Edit: No it wont work as the contains method checks for the existence of a particular Object. Your string, even with the same value, is a different Object. So yes either a) override the contains method, e.g loop through the array and do a string comparison or b) simpler, use a Set.
Edit 2: Apparently it will work per comments below as ArrayList.contains() checks for equality (so you will get a string match), but this seems to disagree with the top voted answer that says it wont.
If all your formats are lower case, then toLowerCase combined with a HashSet is the preferred solution.
If your formats are in mixed case (and shall stay this way, as you are using them for other things, too) you need a real case-insensitive comparison.
Then a TreeSet (or other SortedSet) with a case insensitive collator as the comparator will do. (It is not as fast as a HashSet, but will still be faster then the ArrayList (except for really small lists).)
Alternatively a HashSet variant using a custom hashCode and equals (or simply a normal HashSet on wrapper objects with a case insensitive implementation of equals and hashCode) would do fine.
Add this extended List class:
private static class ListIgnoreCase<String> extends java.util.LinkedList {
public ListIgnoreCase(Collection<String> c) {
super();
addAll(c);
}
public boolean containsIgnoreCase(java.lang.String toSearch) {
for (Object element : this)
if (java.lang.String.valueOf(element).equalsIgnoreCase(toSearch))
return true;
return false;
}
}
Now you can call asList like this:
if(new ListIgnoreCase(Arrays.asList("odt","ott","oth","odm"))
.containtsIgnoreCase(extension)) {
...
You can use IteracleUtils and Predicate from collections4 (apache).
List<String> pformats= Arrays.asList("odt","ott","oth","odm","sxw","stw","sxg","doc","dot","xml","docx","docm","dotx","dotm","doc","wpd","wps","rtf","txt","csv","sdw","sgl","vor","uot","uof","jtd","jtt","hwp","602","pdb","psw","ods","ots","sxc","stc","xls","xlw","xlt","xlsx","xlsm","xltx","xltm","xlsb","wk1","wks","123","dif","sdc","vor","dbf","slk","uos","pxl","wb2","odp","odg","otp","sxi","sti","ppt","pps","pot","pptx","pptm","potx","potm","sda","sdd","sdp","vor","uop","cgm","bmp","dxf","emf","eps","met","pbm","pct","pcd","pcx","pgm","plt","ppm","psd","ras","sda","sdd","sgf","sgv","svm","tgs","tif","tiff","vor","wmf","xbm","xpm","jpg","jpeg","gif","png","pdf","log");
Predicate<String> predicate = (s) -> StringUtils.equalsIgnoreCase(s, "JPG");
if(IterableUtils.matchesAny(pformats, predicate))
// do stuff
}
org.apache.commons.collections4.IterableUtils

Assert that two java beans are equivalent

This question is close, but still not what I want. I'd like to assert in a generic way that two bean objects are equivalent. In case they are not, I'd like a detailed error message explaining the difference instead of a boolean "equal" or "not equal".
import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;
#Test
public void beansAreTheSame(){
MyDomianClass bean1 = new MyDomainClass();
MyDomianClass bean2 = new MyDomainClass();
//TODO - some more test logic
assertThat(bean1, samePropertyValuesAs(bean2));
}
I recommend you use unitils library:
http://www.unitils.org/tutorial-reflectionassert.html
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;
}
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
See also:
Is there a Java reflection utility to do a deep comparison of two objects?
NUnit - Assert to check all properties are equal?
You can use Commons Lang's ToStringBuilder to convert both of them into readable strings and then use assertEquals() on both strings.
If you like XML, you can use java.lang.XMLEncoder to turn your bean into XML and then compare the two XML documents.
Personally, I prefer ToStringBuilder since it gives you more control over the formatting and allows you to do things like sorting the elements in a set to avoid false negatives.
I suggest to put each field of the bean in a different line to make it much more simple to compare them (see my blog for details).
You can set all fields like this:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.Matchers.is;
#Test
public void test_returnBean(){
arrange();
MyBean myBean = act();
assertThat(myBean, allOf(hasProperty("id", is(7L)),
hasProperty("name", is("testName1")),
hasProperty("description", is("testDesc1"))));
}
I think, the most generic approach is to reflect the bean members and test them for equality one-by-one. The common lang's EqualsBuilder is a good start and it should be not a big deal, to adapt it (on source level) to your requirements (reporting the differences instead of returning the equals result).
For unit testing this can be done with JUnit and Mockito using ReflectionEquals. When implementing in the following manner, it will dump the JSON representations of the objects when any fields are not equal which makes it easy to find the offending difference.
import static org.junit.Assert.assertThat;
import org.mockito.internal.matchers.apachecommons.ReflectionEquals;
assertThat("Validating field equivalence of objects", expectedObjectValues, new ReflectionEquals(actualObjectValues));
Since you didn't like the answers in the question you referenced, why not just have a toXml method in each bean, turn them into an xml file and then use xmlUnit to compare.
You can get more info on comparing xml files here:
Best way to compare 2 XML documents in Java
You're not really asserting equality, more doing a "diff". Clearly, the meaning of "same" depends upon particular logic for each type, and the representation of the difference also may vary. One major difference between this requirment and a conventional equals() is that usually equals() will stop as soon as the first difference is seen, you will want to carry on and compare every field.
I would look at reusing some of the equals() patterns, but I suspect you'll need to write your own code.
I am assuming here that both beans are of the same type, in which case only the member variable values will differ across bean instances.
Define an util class (public static final with private ctor) called, say, BeanAssertEquals. Use Java reflection to obtain the value of each member variable in each bean. Then do an equals() between values for the same member variable in different beans. If an equality fails, mention the field name.
Note: member variables are usually private, so you would need to use reflection to temporarily change the accessibility of private members.
Additionally, depending how fine-grained you want the assertion to work, you should consider the following:
Equality of member variables not in the bean class but all superclasses.
Equality of elements in arrays, in case a member variable is of type array.
For two values of a given member across beans, you might consider doing BeanAssertEquals.assertEquals(value1, value2) instead of value1.equals(value2).
(to build on my comment to Andreas_D above)
/** Asserts two objects are equals using a reflective equals.
*
* #param message The message to display.
* #param expected The expected result.
* #param actual The actual result
*/
public static void assertReflectiveEquals(final String message,
final Object expected, final Object actual) {
if (!EqualsBuilder.reflectionEquals(expected, actual)) {
assertEquals(message,
reflectionToString(expected, ToStringStyle.SHORT_PREFIX_STYLE),
reflectionToString(actual, ToStringStyle.SHORT_PREFIX_STYLE));
fail(message + "expected: <" + expected + "> actual: <" + actual + ">");
}
}
This is what I use, and I believe it meets all basic requirements. By doing the assert on the reflective ToString then Eclipse will highlight the difference.
While Hamcrest can offer a much nicer message, this does involve a good deal less code.
The first quesion I'd have to ask if is, do you want to do 'deep' equals on the Bean? does it have child beans that need to be tested? You can override the equals method, but this only returns a boolean, so you could create a 'comparator' and that could throw an exception with a message about what was not equal.
In the following examples, I've listed a few ways to implement the equals method.
if you want to check if they are the same object instance, then the normal equals method from Object will tell you.
objectA.equals(objectB);
if you want to write a customer equals method to check that all the member varibles of an object make them equal then you can override the equals method like this...
/**
* Method to check the following...
* <br>
* <ul>
* <li>getTitle</li>
* <li>getInitials</li>
* <li>getForename</li>
* <li>getSurname</li>
* <li>getSurnamePrefix</li>
* </ul>
*
* #see java.lang.Object#equals(java.lang.Object)
*/
#Override
public boolean equals(Object obj)
{
if ( (!compare(((ICustomer) obj).getTitle(), this.getTitle()))
|| (!compare(((ICustomer) obj).getInitials(), this.getInitials()))
|| (!compare(((ICustomer) obj).getForename(), this.getForename()))
|| (!compare(((ICustomer) obj).getSurname(), this.getSurname()))
|| (!compare(((ICustomer) obj).getSurnamePrefix(), this.getSurnamePrefix()))
|| (!compare(((ICustomer) obj).getSalutation(), this.getSalutation())) ){
return false;
}
return true;
}
The last option is to use java reflection to check all the member varibles in the equals method. This is great if you really want to check every member varible via its bean get/set method. It wont (I dont think) allow you to check private memeber varibles when testing of the two objects are the same. (not if your object model has a circular dependancy, dont do this, it will never return)
NOTE: this is not my code, it comes from...
Java Reflection equals
public static boolean equals(Object bean1, Object bean2)
{
// Handle the trivial cases
if (bean1 == bean2)
return true;
if (bean1 == null)
return false;
if (bean2 == null)
return false;
// Get the class of one of the parameters
Class clazz = bean1.getClass();
// Make sure bean1 and bean2 are the same class
if (!clazz.equals(bean2.getClass()))
{
return false;
}
// Iterate through each field looking for differences
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
// setAccessible is great (encapsulation
// purists will disagree), setting to true
// allows reflection to have access to
// private members.
fields[i].setAccessible(true);
try
{
Object value1 = fields[i].get(bean1);
Object value2 = fields[i].get(bean2);
if ((value1 == null && value2 != null) ||
(value1 != null && value2 == null))
{
return false;
}
if (value1 != null &&
value2 != null &&
!value1.equals(value2))
{
return false;
}
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
return true;
The one thing that this does not do it to tell you the reason for the difference, but that could be done via message to Log4J when you find a section that is not equal.
The xtendbeans library could be of interest in this context:
AssertBeans.assertEqualBeans(expectedBean, actualBean);
This produces a JUnit ComparisonFailure à la:
expected:
new Person => [
firstName = 'Homer'
lastName = 'Simpson'
address = new Address => [
street = '742 Evergreen Terrace'
city = 'SpringField'
]
]
but was:
new Person => [
firstName = 'Marge'
lastName = 'Simpson'
address = new Address => [
street = '742 Evergreen Terrace Road'
city = 'SpringField'
]
]
You could also use it just to get the textual representation for other purposes:
String beanAsLiteralText = new XtendBeanGenerator().getExpression(yourBean)
With this library you can use the above syntactically valid object initialization code fragment to copy/paste it into a (Xtend) source class for the expectedBean, but you don't not have to, it can perfectly well be used without Xtend as well.

What are the alternatives to comparing the equality of two objects?

http://leepoint.net/notes-java/data/expressions/22compareobjects.html
It turns out that defining equals()
isn't trivial; in fact it's moderately
hard to get it right, especially in
the case of subclasses. The best
treatment of the issues is in
Horstmann's Core Java Vol 1.
If equals() must always be overridden, then what is a good approach for not being cornered into having to do object comparison? What are some good "design" alternatives?
EDIT:
I'm not sure this is coming across the way that I had intended. Maybe the question should be more along the lines of "Why would you want to compare two objects?" Based upon your answer to that question, is there an alternative solution to comparison? I don't mean, a different implementation of equals. I mean, not using equality at all. I think the key point is to start with that question, why would you want to compare two objects.
If equals() must always be overridden,
then what is a good approach for not
being cornered into having to do
object comparison?
You are mistaken. You should override equals as seldom as possible.
All this info comes from Effective Java, Second Edition (Josh Bloch). The first edition chapter on this is still available as a free download.
From Effective Java:
The easiest way to avoid problems is
not to override the equals method, in
which case each instance of the class
is equal only to itself.
The problem with arbitrarily overriding equals/hashCode is inheritance. Some equals implementations advocate testing it like this:
if (this.getClass() != other.getClass()) {
return false; //inequal
}
In fact, the Eclipse (3.4) Java editor does just this when you generate the method using the source tools. According to Bloch, this is a mistake as it violates the Liskov substitution principle.
From Effective Java:
There is no way to extend an
instantiable class and add a value
component while preserving the equals
contract.
Two ways to minimize equality problems are described in the Classes and Interfaces chapter:
Favour composition over inheritance
Design and document for inheritance or else prohibit it
As far as I can see, the only alternative is to test equality in a form external to the class, and how that would be performed would depend on the design of the type and the context you were trying to use it in.
For example, you might define an interface that documents how it was to be compared. In the code below, Service instances might be replaced at runtime with a newer version of the same class - in which case, having different ClassLoaders, equals comparisons would always return false, so overriding equals/hashCode would be redundant.
public class Services {
private static Map<String, Service> SERVICES = new HashMap<String, Service>();
static interface Service {
/** Services with the same name are considered equivalent */
public String getName();
}
public static synchronized void installService(Service service) {
SERVICES.put(service.getName(), service);
}
public static synchronized Service lookup(String name) {
return SERVICES.get(name);
}
}
"Why would you want to compare two objects?"
The obvious example is to test if two Strings are the same (or two Files, or URIs). For example, what if you wanted to build up a set of files to parse. By definition, the set contains only unique elements. Java's Set type relies on the equals/hashCode methods to enforce uniqueness of its elements.
I don't think it's true that equals should always be overridden. The rule as I understand it is that overriding equals is only meaningful in cases where you're clear on how to define semantically equivalent objects. In that case, you override hashCode() as well so that you don't have objects that you've defined as equivalent returning different hashcodes.
If you can't define meaningful equivalence, I don't see the benefit.
How about just do it right?
Here's my equals template which is knowledge applied from Effective Java by Josh Bloch. Read the book for more details:
#Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
// only do this if you are a subclass and care about equals of parent
if(!super.equals(obj)) {
return false;
}
if(obj == null || getClass() != obj.getClass()) {
return false;
}
final YourTypeHere other = (YourTypeHere) obj;
if(!instanceMember1.equals(other.instanceMember1)) {
return false;
}
... rest of instanceMembers in same pattern as above....
return true;
}
Mmhh
In some scenarios you can make the object unmodifiable ( read-only ) and have it created from a single point ( a factory method )
If two objects with the same input data ( creation parameters ) are needed the factory will return the same instance ref and then using "==" would be enough.
This approach is useful under certain circumstances only. And most of the times would look overkill.
Take a look at this answer to know how to implement such a thing.
warning it is a lot of code
For short see how the wrapper class works since java 1.5
Integer a = Integer.valueOf( 2 );
Integer b = Integer.valueOf( 2 );
a == b
is true while
new Integer( 2 ) == new Integer( 2 )
is false.
It internally keeps the reference and return it if the input value is the same.
As you know Integer is read-only
Something similar happens with the String class from which that question was about.
Maybe I'm missing the point but the only reason to use equals as opposed to defining your own method with a different name is because many of the Collections (and probably other stuff in the JDK or whatever it's called these days) expect the equals method to define a coherent result. But beyond that, I can think of three kinds of comparisons that you want to do in equals:
The two objects really ARE the same instance. This makes no sense to use equals because you can use ==. Also, and correct me if I've forgotten how it works in Java, the default equals method does this using the automatically generated hash codes.
The two objects have references to the same instances, but are not the same instance. This is useful, uh, sometimes... particularly if they are persisted objects and refer to the same object in the DB. You would have to define your equals method to do this.
The two objects have references to objects that are equal in value, though they may or may not be the same instances (in other words, you compare values all the way through the hierarchy).
Why would you want to compare two objects? Well, if they're equal, you would want to do one thing, and if they're not, you would want to do something else.
That said, it depends on the case at hand.
The main reason to override equals() in most cases is to check for duplicates within certain Collections. For example, if you want to use a Set to contain an object you have created you need to override equals() and hashCode() within your object. The same applies if you want to use your custom object as a key in a Map.
This is critical as I have seen many people make the mistake in practice of adding their custom objects to Sets or Maps without overriding equals() and hashCode(). The reason this can be especially insidious is the compiler will not complain and you can end up with multiple objects that contain the same data but have different references in a Collection that does not allow duplicates.
For example if you had a simple bean called NameBean with a single String attribute 'name', you could construct two instances of NameBean (e.g. name1 and name2), each with the same 'name' attribute value (e.g. "Alice"). You could then add both name1 and name2 to a Set and the set would be size 2 rather than size 1 which is what is intended. Likewise if you have a Map such as Map in order to map the name bean to some other object, and you first mapped name1 to the string "first" and later mapped name2 to the string "second" you will have both key/value pairs in the map (e.g. name1->"first", name2->"second"). So when you do a map lookup it will return the value mapped to the exact reference you pass in, which is either name1, name2, or another reference with name "Alice" that will return null.
Here is a concrete example preceded by the output of running it:
Output:
Adding duplicates to a map (bad):
Result of map.get(bean1):first
Result of map.get(bean2):second
Result of map.get(new NameBean("Alice"): null
Adding duplicates to a map (good):
Result of map.get(bean1):second
Result of map.get(bean2):second
Result of map.get(new ImprovedNameBean("Alice"): second
Code:
// This bean cannot safely be used as a key in a Map
public class NameBean {
private String name;
public NameBean() {
}
public NameBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
}
// This bean can safely be used as a key in a Map
public class ImprovedNameBean extends NameBean {
public ImprovedNameBean(String name) {
super(name);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if(obj == null || getClass() != obj.getClass()) {
return false;
}
return this.getName().equals(((ImprovedNameBean)obj).getName());
}
#Override
public int hashCode() {
return getName().hashCode();
}
}
public class MapDuplicateTest {
public static void main(String[] args) {
MapDuplicateTest test = new MapDuplicateTest();
System.out.println("Adding duplicates to a map (bad):");
test.withDuplicates();
System.out.println("\nAdding duplicates to a map (good):");
test.withoutDuplicates();
}
public void withDuplicates() {
NameBean bean1 = new NameBean("Alice");
NameBean bean2 = new NameBean("Alice");
java.util.Map<NameBean, String> map
= new java.util.HashMap<NameBean, String>();
map.put(bean1, "first");
map.put(bean2, "second");
System.out.println("Result of map.get(bean1):"+map.get(bean1));
System.out.println("Result of map.get(bean2):"+map.get(bean2));
System.out.println("Result of map.get(new NameBean(\"Alice\"): "
+ map.get(new NameBean("Alice")));
}
public void withoutDuplicates() {
ImprovedNameBean bean1 = new ImprovedNameBean("Alice");
ImprovedNameBean bean2 = new ImprovedNameBean("Alice");
java.util.Map<ImprovedNameBean, String> map
= new java.util.HashMap<ImprovedNameBean, String>();
map.put(bean1, "first");
map.put(bean2, "second");
System.out.println("Result of map.get(bean1):"+map.get(bean1));
System.out.println("Result of map.get(bean2):"+map.get(bean2));
System.out.println("Result of map.get(new ImprovedNameBean(\"Alice\"): "
+ map.get(new ImprovedNameBean("Alice")));
}
}
Equality is fundamental to logic (see law of identity), and there's not much programming you can do without it. As for comparing instances of classes that you write, well that's up to you. If you need to be able to find them in collections or use them as keys in Maps, you'll need equality checks.
If you've written more than a few nontrivial libraries in Java, you'll know that equality is hard to get right, especially when the only tools in the chest are equals and hashCode. Equality ends up being tightly coupled with class hierarchies, which makes for brittle code. What's more, no type checking is provided since these methods just take parameters of type Object.
There's a way of making equality checking (and hashing) a lot less error-prone and more type-safe. In the Functional Java library, you'll find Equal<A> (and a corresponding Hash<A>) where equality is decoupled into a single class. It has methods for composing Equal instances for your classes from existing instances, as well as wrappers for Collections, Iterables, HashMap, and HashSet, that use Equal<A> and Hash<A> instead of equals and hashCode.
What's best about this approach is that you can never forget to write equals and hash method when they are called for. The type system will help you remember.

Categories

Resources