Equals
From APIDesign
Line 44: | Line 44: | ||
public boolean equals(Object o) { | public boolean equals(Object o) { | ||
if (o instanceof Date) { | if (o instanceof Date) { | ||
- | if (o.getClass() = | + | if (o.getClass() != getClass() && getClass().isAssignableFrom(o.getClass())) { |
- | return | + | return o.equals(this); // [1] |
} else { | } else { | ||
- | return | + | return time == ((Date)o).time; |
} | } | ||
} | } | ||
Line 59: | Line 59: | ||
public boolean equals(Object o) { | public boolean equals(Object o) { | ||
if (o instanceof Interval) { | if (o instanceof Interval) { | ||
- | if (o.getClass() = | + | if (o.getClass() != getClass() && getClass().isAssignableFrom(o.getClass())) { |
- | return | + | return o.equals(this); // [1] |
} else { | } else { | ||
- | return o. | + | return time == ((Interval)o).time && length == ((Interval)o).length; |
} | } | ||
- | |||
- | |||
} | } | ||
+ | return length == 0 && super.equals(o); // [2] | ||
} | } | ||
} | } | ||
Line 77: | Line 76: | ||
assert new Date(323).equals(new SerializableDate(323)); | assert new Date(323).equals(new SerializableDate(323)); | ||
</source> | </source> | ||
+ | |||
+ | The check ''[1]'' makes sure the comparing is always done by subclass. Then the subclass can either do nothing (like the ''SerializableDate'') and inherits working equals from the superclass, or the subclass can use its special knowledge (like ''Interval'' at line ''[2]'') and be equal to super class in some special conditions (here when the ''length'' is zero). |
Revision as of 08:43, 6 September 2013
Writing equals method in OOP languages can be tricky. The Object.equals documentation suggest that the relation should be symetric but that is hard to enforce. Anyway implementation should at least try. However how should one get ready for subclasses? E.g. make sure instance of following class:
class Date { long time; public boolean equals(Object o) { if (o instanceof Date) { return ((Date)o).time == time; } return false; } }
does not return true when compared to a subclass like:
class Interval extends Date { int length; } assert !new Date(323).equals(new Interval(323, 10));
One way to do it is to restrict the equals only to own type. E.g.:
public boolean equals(Object o) { if (o != null && o.getClass() == Date.class) { return ((Date)o).time == time; } return false; } }
This is approach suitable for algebraic types, but in OOP we might want intervals of length zero to be equal to the Date object with the same beginning. Then it comes to question: Who knows more? (also discussed in SuperVsInner essay). In Java it is safe to assume that subclasses know more - as such it should be the subclass who handles the equals:
class Date { long time; public boolean equals(Object o) { if (o instanceof Date) { if (o.getClass() != getClass() && getClass().isAssignableFrom(o.getClass())) { return o.equals(this); // [1] } else { return time == ((Date)o).time; } } return false; } } class Interval extends Date { int length; public boolean equals(Object o) { if (o instanceof Interval) { if (o.getClass() != getClass() && getClass().isAssignableFrom(o.getClass())) { return o.equals(this); // [1] } else { return time == ((Interval)o).time && length == ((Interval)o).length; } } return length == 0 && super.equals(o); // [2] } } class SerializableDate extends Date implements java.io.Serializable { } assert !new Date(323).equals(new Interval(323, 10)); assert new Date(323).equals(new Interval(323, 0)); assert new Date(323).equals(new SerializableDate(323));
The check [1] makes sure the comparing is always done by subclass. Then the subclass can either do nothing (like the SerializableDate) and inherits working equals from the superclass, or the subclass can use its special knowledge (like Interval at line [2]) and be equal to super class in some special conditions (here when the length is zero).