DOC PREVIEW
MIT 6 170 - Identity and Equality I

This preview shows page 1-2 out of 6 pages.

Save
View full document
View full document
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience

Unformatted text preview:

Lecture 12 : Identity and Equality I 13.1 Quote of the Day I like pigs. Dogs look up to us. Cats look down on us. Pigs treat us as equals. Winston Churchill 13.2 Context What you’ll learn: Object equality and hash codes; how to meet the object contract. Why you should learn this: As your objects are shuffled around in your program, into and out of collections for example, they will from time to time be tested for equality; if you haven’t thought carefully about what this means for your classes, the result will be bugs whose effects are very non-local and hard to trace. What I assume you already know: Proficient in reading specifications, somewhat familiar with the Java collection classes and the use of hash maps. 13.3 The Object Contract In Java, every class extends Object, and therefore inherits all of its methods. Two of these are particularly important and consequential in all programs: the method for testing equality, and the method for generating a hash code. public boolean equals(Object o); public int hashCode(); Like any other methods of a superclass, these methods can be overridden. We will see in lecture next week that a subclass should be a subtype. This means that it should behave according to the specification of the superclass, so that an object of the subclass can be placed in a context in which a superclass object is expected, and still behave appropriately. So a class that overrides equals and hashCode should scrupulously obey their specification. The specification of the Object class given in its documentation is rather abstract and may seem abstruse. But failing to obey it has dire consequences, and tends to result in horribly obscure bugs. Worse, if you do not understand this specification and its ramifications, you are likely to introduce flaws in your code that have a pervasive effect and are hard to eliminate without major reworking. The specification of the Object class is so important that it is often referred to as ‘The Object Contract’. The contract can be found in the method specifications for equals and hashCode in the Java API documentation. It states that: 1� equals must define an equivalence relation – i.e. be reflexive, symmetric, and transitive; � equals must be consistent: repeated calls to the method must yield the same result unless the arguments are modified in between; � for a non-null reference x, x.equals (null) should return false; and � hashCode must produce the same result for two objects that are deemed equal by the equals method. 13.4 Equality and Inheritance For the moment, let us ignore the hashCode method and look first at the properties of the equals method. Reflexivity means that an object always equals itself; symmetry means that when a equals b, b equals a; transitivity means that when a equals b and b equals c, a also equals c. These may seems like obvious properties, and indeed they are. If they did not hold, it is hard to imagine how the equals method would be used: you would have to worry about whether to write a.equals(b) or b.equals(a), for example, if it were not symmetric. What is much less obvious, however, is how easy it is to break these properties inadvertently. The following example shows how symmetry and transitivity can be broken in the presence of inheritance. Consider a simple class that stores a duration in time, with a field for the number of days and the number of seconds: public class Duration { private final int day; private final int sec; public Duration(int day, int sec) { this.day = day; this.sec = sec; } public boolean equals(Object o) { if (!(o instanceof Duration)) return false; Duration d = (Duration) o; return d.day == day && d.sec == sec; } } The equals method takes an Object as its argument; this is mandated by the object contract. The method returns true if the object passed is a Duration and stores the same interval (assume for now that the day and sec fields need to be exactly the same for two Duration objects to be considered equal). Now suppose find that we sometimes have a need for more precision, and we add a field for nanoseconds in a derived class: public class NanoDuration extends Duration { private final int nano; public NanoDuration(int day, int sec, int nano) { super(day,sec); this.nano = nano; } } 2What should the equals method of NanoDuration look like? We could just inherit equals from Duration, but then two NanoDurations will be deemed equal even if they do indeed differ by nanoseconds. We could override it like this: public boolean equals(Object o) { if (!(o instanceof NanoDuration)) return false; NanoDuration nd = (NanoDuration) o; return super.equals(nd) && nano == nd.nano; } This seemingly inoffensive method actually violates the requirement of symmetry. To see why, consider a Duration and a NanoDuration: Duration d = new Duration(1,12); NanoDuration nd = new NanoDuration(1,12,123); System.out.println(d.equals(nd)); // true System.out.println(nd.equals(d)); // false! – not symmetric Notice that d.equals(nd) returns true, but nd.equals(d) returns false! The problem is that these two expressions use different equals methods: the first uses the method from Duration, which ignores nanoseconds, and the second uses the method from NanoDuration. We could try to fix this by having the equals method of NanoDuration ignore nanoseconds when comparing against a normal Duration: public boolean equals(Object o) { if (!(o instanceof Duration)) return false; // if o is a normal Duration, compare without nano field if (!(o instanceof NanoDuration)) return super.equals(o); NanoDuration nd = (NanoDuration) o; return super.equals(nd) && nano == nd.nano; } This solves the symmetry problem, but now equality isn’t transitive! To see why, consider con-structing these points: NanoDuration d1 = new NanoDuration(1,12,123); Duration d2 = new Duration(1,12); NanoDuration d3 = new NanoDuration(1,12,999); System.out.println(d1.equals(d2)); // true System.out.println(d2.equals(d3)); // true System.out.println(d1.equals(d3)); // false! – not transitive The calls p1.equals(p2) and p2.equals(p3) will both return true, but p1.equals(p3) will return false. 3It turns out there is no solution to this problem: it is a


View Full Document

MIT 6 170 - Identity and Equality I

Download Identity and Equality I
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Identity and Equality I and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Identity and Equality I 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?