Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Question about overriding equals()?

Status
Not open for further replies.

cpjust

Programmer
Sep 23, 2003
2,132
0
0
US
I'm putting objects into a LinkedHashSet, and I don't want any duplicate objects added (i.e. object with same data, not necessary exact same object).

From what I've read, LinkedHashSet should prevent duplicate objects from being added (if I'm wrong, just let me know).

I'm guessing LinkedHashSet<T> uses either T.equals() or T.hashCode() (or maybe both?) to determine whether the new T that you're adding is unique; so I tried overriding those 2 functions:
Code:
public class UserINIData
{
	public String UserID;
...
	@Override
	public boolean equals( [b]UserINIData[/b]  rhs )  // Error
	{
		return UserID.equals( rhs.UserID );
	}

	@Override
	public int hashCode()
	{
		return UserID.hashCode();
	}
}
However, I'm getting an error with my equals() function. I see that Object.equals() takes an Object parameter instead of a generic T parameter. Does anyone know why they didn't genericize equals() when they added generics?

Do I need to take an Object instead and just cast it, or will it work fine the way it is (once I take out the @Override annotation)?
 
IMHO, because you can compare two objects, whatever class they belong to. Maybe for you an object from class Car and an object from class Vehicle can be equal.

So, yes, I think you should cast your object.

Cheers,
Dian
 
I just hate casting things unless I have absolutely no choice, since casting just prevents the compiler from doing proper type checking that could prevent a lot of bugs... :-(

If I leave my equals() the way it is, and a class tries to call it in a stupid way like:
Code:
if ( userData.equals( (Object)rhs ) == true )
will that still call my equals() or will it call the default Object.equals() function?
 
That will always call your equals method.

Cheers,
Dian
 
I don't believe that is correct.
Since I had nothing better to do right now, I wrote this simple program to see for myself... Here is the outcome:
Code:
public class CBase
{
	public String Equals( Object  obj )
	{
		return "CBase.Equals()";
	}
}

public class CDerived extends CBase
{
	public String Equals( Object  str )
	{
		return (String)str + ".Equals(Object)";
	}

	public String Equals( String  str )
	{
		return str + ".Equals(String)";
	}
}

public class Test
{
	public static void main( String[]  args )
	{
		CBase base = new CDerived();
		System.out.println( "Running: " + base.Equals( "CDerived" ) );

		CDerived derived = new CDerived();
		System.out.println( "Running: " + derived.Equals( "CDerived" ) );
		System.out.println( "Running: " + derived.Equals( (Object)"CDerived" ) );
	}
}
The output of this program is this:
Code:
Running: CDerived.Equals(Object)
Running: CDerived.Equals(String)
Running: CDerived.Equals(Object)
The 3rd output line would seem to prove my fears correct. The output from the first call to Equals() also worried me, since I didn't even have to cast to (Object) to produce that result. So I decided to comment out the CDerived.Equals(Object) version of the function and re-run the test. I got these results:
Code:
Running: CBase.Equals()
Running: CDerived.Equals(String)
Running: CBase.Equals()
 
Sorry, my bad. I thought you were overriding the equals method from Object class, didn't notice the different parameter types.

In this case, you're rigth, depenfing on the cast the called method will be different, as both get different types.

Cheers,
Dian
 
Just out of curiosity, does anyone know if there's a reason Sun kept equals() the same instead of changing it to something like:
Code:
public <T> boolean equals( T  obj )
 
Does anyone know why they didn't genericize equals() when they added generics?
Maybe, because it would introduce more new problems than solving old problems?

To write
Code:
 class Foo
{
	public boolean equals (Foo f) 
	{
		// ...
	}
}
, you don't need generics at all.

To use equals in its intendet way, you have to stick to the general contract, which includes among other things:

if a.equals (b) then b.equals (a).

If you narrow equals, to only accept objects of this.class:
Code:
 class FooBar extends Foo 
{
	public boolean equals (FooBar f) 
	{
		// ...
	}
}
foobar.equals (foo) wouldn't match the Foobar:equals.
Well - okay, the parent has a method equals (Foo).
This could be called.
Now the parent has to compare a Foo, which in fact is a Foobar, to another Foo (which is a plain Foo), without any knowledge about Foobars.

Now what did we get, which we hadn't before, when using an (Object o) Parameter?

An example a little more complicated might show the problem more clearly:
Code:
 public class Foo
{
	int i;
	
	public Foo (int param)
	{
		i = param;
	}

	public static void main (String args[])
	{
		Foo f = new Foo (7);
		Foobar fb = new Foobar (7, 42);
		
		Foobar fb2 = new Foobar (2, 88);
		Foo fb3 = fb2;
		System.out.println (f.equals (fb));
		System.out.println (fb.equals (f));
		System.out.println (fb2.equals (fb3));
		System.out.println (fb3.equals (fb2));
	}

	public boolean equals (Object o) 
	{
		System.out.print ("Foo:equals ");
		return (o instanceof Foo) && i == ((Foo)o).i;
	}
/*	
	public boolean equals (Foo f) 
	{
		System.out.print ("Foo:equals ");
		return (i == f.i);
	}
*/
}

class Foobar extends Foo
{
	int j;

	public Foobar (int p1, int p2)
	{
		super (p1);
		j = p2;
	}
	
	public boolean equals (Object o) 
	{
		System.out.print ("Foobar:equals ");
		return (o instanceof Foobar) && i == ((Foo)o).i && j == ((Foobar)o).j;
	}
	/*
	public boolean equals (Foobar fb) 
	{
		System.out.print ("Foobar:equals ");
		return i== fb.i && j == fb.j;
	}
	*/
}
Try the classes that way, and then use the commented methods.

Of course there might be semantic reasons, to return in equals in the child, what the parentclass would.
But do you know this in advance and can decide it in the parent?

You may write a method fooEqual in Foo, to compare foos and childs of foo in the foo-way, but shouldnt misuse the equals method.

don't visit my homepage:
 
OK thanks.
I guess keeping backwards compatiblity in Java isn't as easy as I thought...
I'm still trying to get used to the strange Java way of doing things. Even though Java & C++ have very similar syntax, it still confuses the hell out of you when you move from one to the other.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top