Search code examples
javaabstract-data-type

Comparison between abstract objects yields wonky error


Numbers a,b;
a = new NumbersType(1);
b = new NumbersType(1);
System.out.println(a.equals(b));

public class NumbersType implements Numbers {
    int intType;
    public NumbersType (int q) {
        this.intType = q;
    }
    public boolean equals(Numbers n) {
        return this == n;
    }
}

public interface Numbers {
    public boolean equals(Numbers n);
}

This prints false, despite both objects being the same thing. Changing return this == n to return this.intType == n.intType yields error "cannot find symbol: variable intType, location: variable n of type Numbers".

I'm fairly new to ADT and still making my way around it, so excuse me if this is a simple question. I'm not sure why it doesn't work, however, and why I cannot reference n.intType but I can this.intType.


Solution

  • intType is declared on NumbersType but n is a Numbers. So you can't necessarily compare them.

    Now, one thing you could do is create a method on the interface that retrieves some value. I say 'some' value instead of 'the' value because the other Numbers may be some other implementation. This is an important aspect of interfaces. If you have a Numbers you cannot know that it is actually a NumbersType. (You can find that out with instanceof but this would not be a good way to program an interface implementation. The interface's declaration should specify its interactions entirely.)

    But first there's kind of a side issue which is that you're declaring an overload of Object#equals. You might be intending to declare an override which is a different thing. For the purpose of my answer, I am going to show an example of both and name the overload something different.

    Now here's the modified interface:

    public interface Numbers {
        public int getIntType();
        public boolean isIntTypeEqual(Numbers n);
    }
    

    Now that you can retrieve the int, you can compare them in the implementation class.

    public class NumbersType
    implements Numbers {
        private int intType;
    
        public NumbersType(int intType) {
            this.intType = intType;
        }
    
        @Override
        public int getIntType() {
            return intType;
        }
    
        @Override
        public boolean isIntTypeEqual(Number n) {
            return intType == n.getIntType();
        }
    
        // overriding hashCode because we are overriding equals
        @Override
        public int hashCode() {
            return intType;
        }
    
        @Override
        public boolean equals(Object o) {
            if(!(o instanceof Numbers))
                return false;
    
            return isIntTypeEqual((Numbers)o);
        }
    }
    

    You may want to read:

    If you are just learning this stuff, equals may not be a method you should be trying to implement. It is contractual and easy to get wrong.

    Another complication is declaring equals in terms of interfaces: because equals must be symmetric, the implementations must override it identically. Otherwise the contract is broken.