-1

I'm trying to overwrite the equals method. Our professor, for some reason, casts the object parameter to the type class (counter).

Could somebody explain to me the logic behind that? If I instead of "Couter that = (Counter) other;" just remove that line and change "that.count" to "other.count", it executes just fine.

public class Counter {

    private int count;

    public Counter() {
        count = 2;
    }

    public boolean equals(Counter other) {
        if (other instanceof Counter) {
            Counter that = (Counter) other;
            return (this.count == that.count);
        } else {
            return false;
        }
    }

    public static void main(String args []) {
        Counter casio = new Counter();
        Counter texas = new Counter();
        System.out.println(casio.equals(texas));
    }
}
alexrnov
  • 2,346
  • 3
  • 18
  • 34
Tom
  • 279
  • 1
  • 11

3 Answers3

3

The signature of your equals method is wrong. In order to override that method, it needs to have a parameter of type Object:

@Override
public boolean equals(Object other) {

And this requires you to cast other to Counter and do an instanceof check, otherwise you cannot access the count field.

f1sh
  • 11,489
  • 3
  • 25
  • 51
  • Whoops just a small mistake, question stands (editing now, thanks!). – Tom Mar 01 '19 at 15:59
  • This answers the question. If `other` has type `Object` then your `equals` method won't compile without the type cast. – Stephen C Mar 01 '19 at 16:01
  • But stephen, it does compile, whether I cast it or not. I don't really get how this requires me to cast other to Counter, any specific reason, why can't I access the count field? In addition, why would I only have to do that to the input parameter and not the object that the equals method is executed on. – Tom Mar 01 '19 at 16:02
  • 1
    This does not compile if you don't cast. If `other` is an `Object`, it has no `count` field which you need for your comparison. – f1sh Mar 01 '19 at 16:03
  • This runs: public boolean equals(Counter other) { if(other instanceof Counter) { return (this.count == other.count); } else { return false; } } – Tom Mar 01 '19 at 16:04
  • But this doesn't: `public boolean equals(Object other) { if(other instanceof Counter) { return (this.count == other.count); } else { return false; } }`. – Stephen C Mar 01 '19 at 16:05
  • 3
    @user472288 Yes, because then the method argument is already of type Counter. But then it doesn't fulfill the contract of Object.equals. – dunni Mar 01 '19 at 16:06
  • Exactly. What you probably are missing is that (unlike you) your professor has probably defined `equals` with the correct signature. But your version has the wrong signature, therefore does not override `Object::equals` ... and therefore won't be used by numerous utility classes, etc that use the standard `equals` method. – Stephen C Mar 01 '19 at 16:08
  • I see, so unless I cast whatever input parameter to the class type, equals will not be overwritten but is essentially a different method that I just implemented, which happens to work in my case? – Tom Mar 01 '19 at 16:10
  • 1
    Yes. It happens to work in your case. But if you put a `Counter` into a `List` and then called `contains` on the list, you would not find it. – Stephen C Mar 01 '19 at 16:11
  • @user472288 exactly. your `casio.equals(texas)` calls your "different" method, which is not overriding anything. You can place the `@Override` annotation above the method to tell the compiler "i want this method to override the superclass's method". – f1sh Mar 01 '19 at 16:12
2

The reason that the cast is necessary is that the implementor of the equals method want to use a field of the class Counter, that is, the field count.

Because the parameter other passed to the method is of type Object. The compiler therefore doesn't know that it's actually a Counter object. With the typecast you tell the compiler that you're sure that the object is actually a Counter instance.

The compiler then allows you to use its defined members, such as count.

Is typecasting always safe?

Note that the casting could normally throw a ClassCastException at runtime, but since first the object's type is checked by the instanceof operator, it is guaranteed that the object is actually a Counter instance, hence it's safe to do this.

Why is explicit casting necessary?

Why is explicit typecasting necessary when we are already checking the type with instanceof? The reason why this explicit cast is necessary, is because instanceof is a runtime check. Casting is a compile-time assertion.


But why not just boolean equals(Counter other)?

That's because then you are overloading Object's equals method, instead of overriding.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • But doesn't the "instanceof" already verify this check and makes sure that the object is actually a counter object? – Tom Mar 01 '19 at 16:08
  • @user472288 Yes, that's why it is safe to cast (see edit). – MC Emperor Mar 01 '19 at 16:09
  • Argh.. but if we verify that this is a counter object, why can't we access the count field without casting the object parameter first? Sorry, I'm just confused about this. – Tom Mar 01 '19 at 16:11
0

There is no logic... except the fact that there is probably a mistake in the equals methods signature. Here equals is a overloading of the equals methods inherited form the Object class.

the right way to define an equals method is to override the equals method inherited form object.

public boolean equals(Object other) {
      if(other instanceof Counter) {
        Counter that = (Counter) other;
        return (this.count == that.count);
      }
      else {
          return false;
      }
}

in this case the cast is mandatory

Philippe G.
  • 135
  • 1
  • 7