tags:

views:

372

answers:

5

Basically i don't want to compare elements, but elements that thy contain. It would be cool if following would work, but it does not.

public boolean equals(Ie<T> that) {
 T This = this.value;
 T That = that.value;
 boolean x = That.compareTo(This) == 0;
 return x;
}

What i have done is :

public boolean equals(Object obj) {
 if (obj == null)
  return false;

 String tmp = obj.toString();
 if (this.value instanceof Integer)
  return equalsInt(new Ie<Integer>(Integer.parseInt(tmp)));
 // etc.

 return false;
}

public boolean equalsInt(Ie<Integer> ie) {
 Integer This = this.value.intValue();
 Integer That = ie.value;
 boolean x = That.compareTo(This) == 0;
 return x;
}

This is a obvious brute-force solution. Can it be done without obj.toString(); + new element?

Edit:

Class Ie is defined as:

public static class Ie<T extends Number & Comparable<? super T>>
  extends Number implements Comparable<Ie<T>> {

Class has rather simple compare to method:

public int compareTo(Ie<T> that) {
 T This = this.value;
 T That = that.value;
 return (This.compareTo(That) < 0 ? -1 : (This
   .compareTo(That) > 0 ? 1 : 0));
}

I am using the class in Collection addall method, where contains is calling equals.

Erikson solution worked fine:

@Override
public boolean equals(Object that) {
 if (that == null)
  return false;
 if (this == that)
  return true;
 if (that instanceof Ie<?>) {
  Object ThisValue = this.value;
  Object ThatValue = ((Ie<?>) that).value;
  return ThisValue.equals(ThatValue);
 }

 return false;
}
A: 
@Override
public boolean equals(Object that)
{
    if (this == that)
      return true;
    if (that instanceof Ie) {
      Object ThisValue = this.value;
      Object ThatValue = ((Ie<?>) that).value;
      return ThisValue.equals(ThatValue);
    } else 
      return false;
}
Matt Ball
This is a confusing equals method, since it doesn't properly override Object.equals(Object other). The parameter of equals() should be Object, which is then checked at runtime to see if it equals this object.
Avi
I like the basic idea here a lot, so I fixed it up to work properly. Hope you don't mind.
erickson
@erickson: ewwwww, instanceOf. But, yes, thanks for the implementation.
Matt Ball
Well, if you do an `equals(Ie<T>)` method, it won't override `Object.equals`, so you'll just have to live with `instanceof`.
gustafc
Well, there other ways to make sure that "that" is the "right" type... depending on what the "right" type means. For example, you might require that `this` and `that` are the same runtime type by checking `getClass().equals(that.getClass())` instead. But, since `value` seems to be all that matters here, I think `instanceof` is appropriate.
erickson
@gustafc: Yes, I know that. instanceOf is just really hackish and inelegant to me. I'd much rather use a solution that takes advantage of OO or at least use reflection, *in general*. I acknowledge that instanceOf is the cleanest way to do it here.
Matt Ball
A: 

First, don't confuse equals() with compareTo(). The first one shows if two objects are equal. Compare on the other hand tries to find which is larger (useful when sorting).

Compare introduces an element of relativity. If, for example, you have a List of Person objects then person "Allun" is smaller then "Dave" if you compare them by name but they might be equal if you compare them by age. Even if they are equal in regards to age, they are NOT equal according to equals() method because they represent different person.

Concerning the equals() method itself, this might help you:

class MyClass {

  int anInt;

  public boolean equals(Object obj) {
    if (obj instance of MyClass) {
      MyClass that = (MyClass) obj;
      return this.anInt==that.anInt;
    }
    return false;
  }
}

Or if you have another object and you want to check for null:

class MyClass {

  Object anObject;

  public boolean equals(Object obj) {
    if (obj instance of MyClass) {
      MyClass that = (MyClass) obj;
      return this.anObject==null ? that.object==null : this.object.equals(that.object);
    }
    return false;
  }
}
idrosid
+1  A: 

For this to work, every type T must implement the Comparable interface, and every class that implements Comparable should implement equals in a consistent manner. So, Matt Ball's answer should suffice.

However, if for some reason it doesn't, a solution that works around Java's type erasure is needed. By explicitly specifying the type of T, the necessary type checking can be done. The Ie class can be defined in part like this:

class Ie<T extends Comparable<T>>
{

  private final Class<? extends T> type;

  private final T value;

  public Ie(Class<? extends T> type, T value)
  {
    this.type = type;
    this.value = value;
  }

  @Override
  public boolean equals(Object obj)
  {
    if (obj == this)
      return true;
    if (obj instanceof Ie) {
      Object that = ((Ie<?>) obj).value;
      if (type.isInstance(that)) {
        return value.compareTo(type.cast(that)) == 0;
      }
    }
    return false;
  }

}
erickson
A: 

Yes, the easiest way is to use the Object.equals method to compare contained objects (like in Matt Ball's answer). However, if you can't use it for some reason, you will have to store the type and do some dynamic type casting (like in erickson's answer), or do unchecked casting, like in the sample below:

class Ie<T extends Comparable<T>>
{
 public Ie(T value) {this.value = value;}

 private final T value;

 public boolean equals(Object obj) {
  if(obj == this)
   return true;

  if(obj instanceof Ie<?>)
  {
   Ie<?> That = (Ie<?>)obj;

   if(value.getClass() != That.value.getClass())
    return false;

   // Cast to anything that allows calling typed equals(). 
   // The type will be erased at runtime anyway.
   return ((Ie<String>)this).equals((Ie<String>)That); 
  }
  return false;
 }

 public boolean equals(Ie<T> that)
 {
  return that.value.compareTo(this.value) == 0;
 }
}
Rotsor
A: 

Consider Ie<String> and Ie<Integer> that is going to throw a ClassCastException if you try and compare instances of the generic argument type.

I'm going to suggest a slightly different solution to erickson. If we add a Comparator to Ie, we can delegate comparison.

On the downside, this does requires that you create a Comparator (possibly with defined equals) and you must not to unsafe case to get the Comparator instance (as, say, Collections.emptyList does).

class Ie<T> {

    private final Comparator<? super T> comparator;

    private final T value;

    public Ie(Comparator<? super T> comparator, T value) {
        if (comparator == null || value == null) {
            throw new NullPointerException();
        }
        this.comparator = comparator;
        this.value = value;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Ie)) {
            return false;
        }
        Object other = (Ie<?>)obj;
        return
             this.comparator.equals(other.comparator) &&
             this.comparator.compare(this.value, (T)other.value) == 0
    }

    @Override
    public int hashCode() {
        return comparator.hashCode()*31 + value.hashCode();
    }
}
Tom Hawtin - tackline