9

I have an interface and I want everyone who implements this interface to implements an overrridden "equals" method.

Is there a way to make sure that happens?

The way I guess this will happen is that the class that implements my interface will automatically get the equals from Object therefore making the interface happy.

Fredrik
  • 10,626
  • 6
  • 45
  • 81
  • 1
    What is your rationale for this? – Thorbjørn Ravn Andersen Jun 10 '10 at 06:44
  • The specific case I ran into when this question arose; I have an interface that several classes implemented. These classes are identified by a unique indentifier. So if two identifiers were the same, the objects should be considered to be equal. At one point I had a list of one of those classes and wanted to know if a new object already existed in the list. I thought it would be elegant to use List.contains(..) for this check. But that would require that I could be certain that equals was overridden. Easy to solve in other ways though. An abstract class would have worked well too. – Fredrik Jun 10 '10 at 11:06

6 Answers6

10

No, you only can create an abstract class instead of an interface like this:

public abstract class MyApi {

  public final boolean equals(Object other) {
    if (other == this) {
      return true;
    }
    if (other instanceof MyApi) {
      return equals((MyApi)other);
    }
    return false;
  }

  protected abstract boolean equals(MyApi other);

}

or a more simple version:

public abstract class MyApi {

  public boolean equals(Object other) {
    throw new UnsupportedOperationException("equals() not overridden: " + getClass());
  }

}

EDIT (gave it a try after the comment from @CodeConfident, thanks! Never assumed it would work):

You can also simply declare equals() in an abstract class (not in an interface!) and therefore hide the Object implementation and enforce a new implementation in any subclass:

public abstract class MyApi {

  public abstract boolean equals(Object obj);

  public abstract int hashCode();

}

Anyway you should always implement equals() and hashCode() together to fulfil the contract.

Arne Burmeister
  • 20,046
  • 8
  • 53
  • 94
  • If you are advocating the use of an abstract class, why not declare equals(Object) to be abstract? – ILMTitan Jun 10 '10 at 14:33
  • 2
    @ilmtitan: you cannot declare a method abstract that is inherited! – Arne Burmeister Jun 10 '10 at 17:39
  • @ArneBurmeister - This is not true (now at least). You absolutely can override an inherited method with an abstract method inside an abstract class. See this answer: https://stackoverflow.com/questions/1718112/tostring-equals-and-hashcode-in-an-interface#answer-1718170 – charles-allen Jul 20 '17 at 07:08
  • Could you not also simplify your first method as `return (other == this) || (other instanceof MyApi && equals((MyApi) other));` (+1 by the way; I like your approach) – charles-allen Jul 20 '17 at 07:11
  • @CodeConfident you are right, I never did that and not even thought it would work, thanks! – Arne Burmeister Jul 20 '17 at 16:01
3

No. You can add it to the interface (and thus the javadocs), but if Object.equals has the same signature, you can't have the compiler make them override it.

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
2

No. An interface is a contract guaranteeing that methods exists.

There is no mechanism for enforcing that methods should be overridden in an interface.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
1

Edit: Probably not a good idea (see comment from FarmBoy). Leaving here for posterity.

Instead of using equals(Object obj) from the Object class, make them compare it to an implementation of your interface.

public interface MyInterface {
  public boolean equals(MyInterface mi);
}

Therefore,

public class MyImplementation implements MyInterface {
  public boolean equals(MyInterface mi)
  {
    if(this == mi)
      return true;
    // for example, let's say that each implementation
    // is like a snowflake...(or something)
    return false;
  }
}

And then:

public class Main {

  public static void main(String[] args)
  {
    Object o = new MyImplementation();
    MyImplementation mi1 = new MyImplementation();
    MyImplementation mi2 = new MyImplementation();

    // uses Object.equals(Object)
    o.equals(mi1);
    // uses MyImplementation.equals(MyInterface)
    mi1.equals(mi2);
    // uses Object.equals(Object)
    mi2.equals(o);
  }
}
Catchwa
  • 5,845
  • 4
  • 31
  • 57
  • 2
    This is not a good idea. Anything that calls equals (like puting an object into a `Set` will not call this equals, but the 'real' one. So you would force the implementing class to implement this method, and any good programmer would implement the 'real' one too. – Eric Wilson Jun 10 '10 at 06:35
  • Good point - I hadn't thought about that. I'd like more detail from the asker as to the motivation behind this question. – Catchwa Jun 10 '10 at 06:39
0

I've experimented with writing the required contract for equals in the JavaDoc. But the requirement that hashCode is consistent with equals results in a very complex contract for hashCode. So I gave up and created abstract base class with a final implementation of the two methods instead.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
0

I guess there can be two reasons why such an equals method might be required

  1. You want to ensure that all the classes in your app (or a subset of them) "will" have the equals method. Something like standards enforcement, or making sure some of the APIs you use work as they should (and they expect equals to be implemented correctly. Say you use Maps quite a lot and want to be absolutely sure that a subset of classes are definitely possible map keys) If that is the case, this is not the way to go. In this case you will not be able to do it, but even if you are, it would not be correct. You should go for code coverage tools, and better unit testing.

  2. You dont want THE equals method, but want a similar method. In this case you can create another method with a similar name in the interface.

Nivas
  • 18,126
  • 4
  • 62
  • 76