CSE 331 Software Design & Implementation

Similar documents
CSE 331 Software Design & Implementation

Expected properties of equality

Announcements. Equality. Lecture 10 Equality and Hashcode. Announcements. CSE 331 Software Design and Implementation. Leah Perlmutter / Summer 2018

Equality. Michael Ernst. CSE 331 University of Washington

Equality. Michael Ernst. CSE 331 University of Washington

MIT EECS Michael Ernst Saman Amarasinghe

The class Object. Lecture CS1122 Summer 2008

equals() in the class Object

Data abstractions: ADTs Invariants, Abstraction function. Lecture 4: OOP, autumn 2003

Introduction to Object-Oriented Programming

java.lang.object: Equality

Methods Common to all Classes

Super-Classes and sub-classes

Programming Languages and Techniques (CIS120)

Programming Languages and Techniques (CIS120)

Canonical Form. No argument constructor Object Equality String representation Cloning Serialization Hashing. Software Engineering

Exam 2. Topics. Preconditions vs. Exceptions. Exam 2 Review. Exam 2 on Thursday, March 29 th. Closed book, closed computer, closed phone

Programming Languages and Techniques (CIS120e)

The Object Class. java.lang.object. Important Methods In Object. Mark Allen Weiss Copyright 2000

Java Fundamentals (II)

Creating an Immutable Class. Based on slides by Prof. Burton Ma

COMP 250 Fall inheritance Nov. 17, 2017

CSE 331 Spring 2018 Midterm

The Liskov Substitution Principle

Implementing Object Equivalence in Java Using the Template Method Design Pattern

CSE 331 Software Design & Implementation

Announcements. Subtype Polymorphism, Subtyping vs. Subclassing, Liskov Substitution Principle. Intuition: Type Signature is a Specification.

CSE 331 Software Design & Implementation

Software Construction

Java: advanced object-oriented features

For this section, we will implement a class with only non-static features, that represents a rectangle

Collections. Powered by Pentalog. by Vlad Costel Ungureanu for Learn Stuff

CSE 331 Software Design & Implementation

COMP 250. Lecture 30. inheritance. overriding vs overloading. Nov. 17, 2017

CSE 331 Software Design and Implementation. Lecture 14 Generics 2

Today. Book-keeping. Inheritance. Subscribe to sipb-iap-java-students. Slides and code at Interfaces.

The Java Type System (continued)

Programming Languages and Techniques (CIS120)

Chapter 11: Collections and Maps

Principles of Software Construction: Objects, Design and Concurrency. Polymorphism, part 2. toad Fall 2012

CSE 331 Software Design and Implementation. Lecture 14 Generics 2

Building Java Programs. Inheritance and Polymorphism

Hash tables. hashing -- idea collision resolution. hash function Java hashcode() for HashMap and HashSet big-o time bounds applications

Programming Languages and Techniques (CIS120)

CSE331 Autumn 2011 Midterm Examination October 28, 2011

INHERITANCE. Spring 2019

Software Engineering Design & Construction

CSE 331 Software Design & Implementation

Top Down Design vs. Modularization

Contents. I. Classes, Superclasses, and Subclasses. Topic 04 - Inheritance

CSE 331 Software Design & Implementation

Inheritance. Lecture 11 COP 3252 Summer May 25, 2017

USAL1J: Java Collections. S. Rosmorduc

Solutions to Quiz 1 (March 14, 2016)

Exam 2 CSCI 2600 Principles of Software November 3, 2015

CMSC131. Inheritance. Object. When we talked about Object, I mentioned that all Java classes are "built" on top of that.

MET08-J. Preserve the equality contract when overriding the equals() method

Menu. In general, what are possible advantages of one design over another? Class 27: Exam 2. Exam 2 Parenthesizing the Expression. Design A.

Review. CSE 143 Java. A Magical Strategy. Hash Function Example. Want to implement Sets of objects Want fast contains( ), add( )

INSTRUCTIONS TO CANDIDATES

Announcements. Lecture 15 Generics 2. Announcements. Big picture. CSE 331 Software Design and Implementation

CSE 331 Software Design and Implementation. Lecture 15 Generics 2

Lecture 7: Data Abstractions

Overloaded Methods. Sending Messages. Overloaded Constructors. Sending Parameters

Principles of Software Construction: Objects, Design and Concurrency. Inheritance, type-checking, and method dispatch. toad

Day 4. COMP1006/1406 Summer M. Jason Hinek Carleton University

Software Development (cs2500)

CSE341, Fall 2011, Lecture 26 Summary

Domain-Driven Design Activity

09/02/2013 TYPE CHECKING AND CASTING. Lecture 5 CS2110 Spring 2013

MIT AITI Lecture 18 Collections - Part 1

Outline. applications of hashing equality and comparisons in Java sorting selection sort bubble sort insertion sort

CS-202 Introduction to Object Oriented Programming

CSE 331 Final Exam 12/14/15 Sample Solution. Question 1. (20 points, 1 each) Warmup. For each statement, circle T if it is true and F if it is false.

CSE 331 Software Design & Implementation

C12a: The Object Superclass and Selected Methods

Binary Relations Part One

CSE 331 Software Design & Implementation

CSE 331 Software Design & Implementation

Outline. Inheritance. Abstract Classes Interfaces. Class Extension Overriding Methods Inheritance and Constructors Polymorphism.


Rules and syntax for inheritance. The boring stuff

Programming Languages and Techniques (CIS120)

CSE331 Winter 2014, Midterm Examination February 12, 2014

Lecture 11 Subtypes and Subclasses

CSE 331 Final Exam 6/5/17. Name UW ID#

CSE 331 Software Design and Implementation. Lecture 12 Subtypes and Subclasses

Goals for Today. CSE1030 Introduction to Computer Science II. CSE1030 Lecture #4. Review: Methods / Code

Not overriding equals

Advanced Programming - JAVA Lecture 4 OOP Concepts in JAVA PART II

cs2010: algorithms and data structures

CS 61B Data Structures and Programming Methodology. July 3, 2008 David Sun

CSE 373. Objects in Collections: Object; equals; compareto; mutability. slides created by Marty Stepp

6.005 Elements of Software Construction Fall 2008

CSE 331 Summer 2017 Final Exam. The exam is closed book and closed electronics. One page of notes is allowed.

CSE wi Midterm Exam 2/8/18 Sample Solution

Defective Java Code: Turning WTF code into a learning experience

Defective Java Code: Turning WTF code into a learning experience

Fall 2017 Mentoring 9: October 23, Min-Heapify This. Level order, bubbling up. Level order, bubbling down. Reverse level order, bubbling up

Object Oriented Programming Part II of II. Steve Ryder Session 8352 JSR Systems (JSR)

Transcription:

CSE 331 Software Design & Implementation Kevin Zatloukal Summer 2017 Identity, equals, and hashcode (Based on slides by Mike Ernst, Dan Grossman, David Notkin, Hal Perkins, Zach Tatlock)

Overview Notions of object equality are important in any language In Java, collection classes depend on equals and hashcode EJ 47: Know and use the libraries every programmer should be familiar with the contents of java.lang and java.util e.g., TreeSet may not work properly if equals is wrong e.g., HashSet may not work properly of hashcode is wrong CSE331 Summer 2017 2

Object equality A simple idea?? Two objects are equal if they have the same value A subtle idea: intuition can be misleading Same object or same contents? Same concrete value or same abstract value? Same right now or same forever? Same for instances of this class or also for subclasses? When are two collections equal? How related to equality of elements? Order of elements? What if a collection contains itself? How can we implement equality efficiently? CSE331 Summer 2017 3

Expected properties of equality Reflexive a.equals(a) == true Confusing if an object does not equal itself Symmetric a.equals(b) iff b.equals(a) Confusing if order-of-arguments matters Transitive a.equals(b) && b.equals(c) => a.equals(c) Confusing again to violate centuries of logical reasoning A relation that is reflexive, transitive, and symmetric is an equivalence relation CSE331 Summer 2017 4

Reference equality Reference equality means an object is equal only to itself a == b only if a and b refer to (point to) the same object Reference equality is an equivalence relation Reflexive Symmetric Transitive Reference equality is the smallest equivalence relation on objects Hardest to show two objects are equal (must be same object) Cannot be smaller without violating reflexivity Sometimes but not always what we want CSE331 Summer 2017 5

What might we want? Date d1 = new Date(12,27,2013); Date d2 = new Date(12,27,2013); Date d3 = d2; // d1==d2? // d2==d3? // d1.equals(d2)? // d2.equals(d3)? d1 d2 d3 month day year month day year 12 27 2013 12 27 2013 Sometimes want equivalence relation bigger than == Java takes OOP approach of letting classes override equals CSE331 Summer 2017 6

Object.equals method public class Object { public boolean equals(object o) { return this == o; Implements reference equality Subclasses can override to implement a different equality But library includes a contract equals should satisfy Reference equality satisfies it So should any overriding implementation Balances flexibility in notion-implemented and what-clientscan-assume even in presence of overriding CSE331 Summer 2017 7

equals specification public boolean equals(object obj) should be: reflexive: for any reference value x, x.equals(x) == true symmetric: for any reference values x and y, x.equals(y) == y.equals(x) transitive: for any reference values x, y, and z, if x.equals(y) and y.equals(z) are true, then x.equals(z) is true consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false (provided neither is mutated) For any non-null reference value x, x.equals(null) should be false CSE331 Summer 2017 8

Why all this? Remember the goal is a contract: weak enough to allow different useful overrides strong enough so clients can assume equal-ish things example: to implement a set this gives a good balance in practice In summary: equivalence relation consistency, but allow for mutation to change the answer asymmetric with null (other way raises exception) final detail: argument of null must return false weird but useful often see, e.g., left.equals(direction) false for null CSE331 Summer 2017 9

An example A class where we may want equals to mean equal contents public class Duration { private final int min; // RI: min>=0 private final int sec; // RI: 0<=sec<60 public Duration(int min, int sec) { assert min>=0 && sec>=0 && sec<60; this.min = min; this.sec = sec; Should be able to implement what we want and satisfy the equals contract CSE331 Summer 2017 10

How about this? public class Duration { public boolean equals(duration d) { return this.min==d.min && this.sec==d.sec; Two bugs: 1. Violates contract for null (not that interesting) Can add if(d==null) return false; But our fix for the other bug will make this unnecessary 2. Does not override Object s equals method (more interesting) CSE331 Summer 2017 11

Overloading versus overriding In Java: A class can have multiple methods with the same name and different parameters (number or type) A method overrides a superclass method only if it has the same name and exact same argument types So Duration s boolean equals(duration d) does not override Object s boolean equals(object d) Sometimes useful to avoid having to make up different method names Sometimes confusing since the rules for what-method-getscalled are complicated CSE331 Summer 2017 12

Example: no overriding public class Duration { public boolean equals(duration d) { Duration d1 = new Duration(10,5); Duration d2 = new Duration(10,5); Object o1 = d1; Object o2 = d2; d1.equals(d2); o1.equals(o2); d1.equals(o2); o1.equals(d2); d1.equals(o1); // true // false(!) // false(!) // false(!) // true [using Object s equals] CSE331 Summer 2017 13

Example fixed (mostly) public class Duration { public boolean equals(object d) { Duration d1 = new Duration(10,5); Duration d2 = new Duration(10,5); Object o1 = d1; Object o2 = d2; d1.equals(d2); o1.equals(o2); d1.equals(o2); o1.equals(d2); d1.equals(o1); // true // true [overriding] // true [overriding] // true [overriding] // true [overriding] CSE331 Summer 2017 14

A little more generally Won t go through all the overloading-resolution rules here In short, Java: Uses (compile-time) types to pick the signature (at compile-time) In example: if receiver or argument has compile-time type Object, then only signature taking an Object is known to work, so it is picked At run-time, uses dynamic dispatch to choose what implementation with that signature runs In un-fixed example: the inherited method is the only one with the take-an-object signature In fixed example: Overriding matters whenever the run-time class of the receiver is Duration CSE331 Summer 2017 15

But wait! This doesn t actually compile: public class Duration { public boolean equals(object o) { return this.min==o.min && this.sec==o.sec; CSE331 Summer 2017 16

Really fixed now public class Duration { public boolean equals(object o) { if(!(o instanceof Duration)) return false; Duration d = (Duration) o; return this.min==d.min && this.sec==d.sec; Cast cannot fail We want equals to work on any pair of objects Gets null case right too (null instanceof C always false) So: rare use of cast that is correct and idiomatic This is what you should do (cf. Effective Java) CSE331 Summer 2017 17

Satisfies the contract public class Duration { public boolean equals(object o) { if(! o instanceof Duration) return false; Duration d = (Duration) o; return this.min==d.min && this.sec==d.sec; Reflexive: Yes Symmetric: Yes, even if o is not a Duration! (Assuming o s equals method satisfies the contract) Transitive: Yes, similar reasoning to symmetric CSE331 Summer 2017 18

Even better Defensive Tip: use the @Override annotation when overriding public class Duration { @Override public boolean equals(object o) { Compiler warning if not actually an override Catches bug where argument is Duration or String or... Alerts reader to overriding Concise, relevant, checked documentation CSE331 Summer 2017 19

Where are we? Done: Understanding the equals contract Implementing equals correctly for Duration override Object.equals(Object) satisfying the contract [for all types of arguments] Alas, matters get worse for subclasses of Duration no perfect universal solution each option involves trade-offs CSE331 Summer 2017 20

Two subclasses class CountedDuration extends Duration { public static numcounteddurations = 0; public CountedDuration(int min, int sec) { super(min,sec); ++numcounteddurations; class NanoDuration extends Duration { private final int nano; public NanoDuration(int min, int sec, int nano){ super(min,sec); this.nano = nano; public boolean equals(object o) { CSE331 Summer 2017 21

CountedDuration is (probably) fine CountedDuration does not override equals inherits Duration.equals(Object) Will (implicitly) treat any CountedDuration like a Duration when checking equals o instanceof Duration is true if o is CountedDuration Any combination of Duration and CountedDuration objects can be compared equal if same contents in min and sec fields works because o instanceof Duration is true when o is an instance of CountedDuration CSE331 Summer 2017 22

NanoDuration is (probably) not fine If we don t override equals in NanoDuration, then objects with different nano fields will be equal Using what we have learned: @Override public boolean equals(object o) { if (!(o instanceof NanoDuration)) return false; NanoDuration nd = (NanoDuration) o; return super.equals(nd) && nano == nd.nano; But we have violated the equals contract Hint: Compare a Duration and a NanoDuration CSE331 Summer 2017 23

The symmetry bug public boolean equals(object o) { if (!(o instanceof NanoDuration)) return false; NanoDuration nd = (NanoDuration) o; return super.equals(nd) && nano == nd.nano; This is not symmetric! Duration d1 = new NanoDuration(5, 10, 15); Duration d2 = new Duration(5, 10); d1.equals(d2); // false d2.equals(d1); // true CSE331 Summer 2017 24

Fixing symmetry This version restores symmetry by using Duration s equals if the argument is a Duration (and not a NanoDuration) public boolean equals(object o) { if (!(o instanceof Duration)) return false; // if o is a normal Duration, compare without nano if (!(o instanceof NanoDuration)) return super.equals(o); NanoDuration nd = (NanoDuration) o; return super.equals(nd) && nano == nd.nano; Alas, this still violates the equals contract Transitivity CSE331 Summer 2017 25

The transitivity bug Duration d1 = new NanoDuration(1, 2, 3); Duration d2 = new Duration(1, 2); Duration d3 = new NanoDuration(1, 2, 4); d1.equals(d2); d2.equals(d3); d1.equals(d3); // true // true // false! NanoDuration Duration NanoDuration min 1 min 1 min 1 sec nano 2 3 sec 2 sec nano 2 4 CSE331 Summer 2017 26

No perfect solution Effective Java says not to (re)override equals like this (unless superclass is non-instantiable) generally good advice but there is one way to satisfy equals contract (see below) Two far-from-perfect approaches on next two slides: 1. Don t make NanoDuration a subclass of Duration 2. Change Duration s equals so only Duration objects that are not (proper) subclasses of Duration are equal CSE331 Summer 2017 27

Option 1: avoid subclassing Choose composition over subclassing (EJ Item 81) often good advice in general (we ll discuss more later on) many programmers overuse subclassing public class NanoDuration { private final Duration duration; private final int nano; Solves some problems: clients can choose which type of equality to use Introduces others: can t use NanoDurations where Durations are expected (since it is not a subtype) CSE331 Summer 2017 28

Option 2: the getclass trick Check if o is a Duration and not a subtype: @Overrides public boolean equals(object o) { // in Duration if (o == null) return false; if (!o.getclass().equals(getclass())) return false; Duration d = (Duration) o; return d.min == min && d.sec == sec; But this breaks CountedDuration! subclasses do not act like instances of superclass because behavior of equals changes with subclasses generally considered wrong to break subtyping like this CSE331 Summer 2017 29

Subclassing summary Subtypes should be useable wherever the type is used Liskov substitution principle Unresolvable tension between what we want for equality: treat subclasses differently what we want for subtyping: treat subclasses the same No perfect solution for all cases choose whether you want subtyping or not in former case, don t override equals (make it final) in latter case, can use composition instead (this matches the advice in Effective Java) (unfortunately) must make one choice for all subtypes CSE331 Summer 2017 30

hashcode Another method in Object: public int hashcode() Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.hashmap. Contract (again essential for correct overriding): Self-consistent: o.hashcode() is fixed (unless o is mutated) Consistent with equality: a.equals(b) implies a.hashcode() == b.hashcode() Want!a.equals(b) implies a.hashcode()!= b.hashcode() but not actually in contract and (not true in most implementations) CSE331 Summer 2017 31

Think of it as a pre-filter If two objects are equal, they must have the same hash code up to implementers of equals and hashcode to satisfy this if you override equals, you must override hashcode If objects have same hash code, they may or may not be equal usually not leads to better performance hashcode in Object tries to (but may not) give every object a different hash code Hash codes are usually cheap[er] to compute, so check first if you usually expect not equal a pre-filter CSE331 Summer 2017 32

Asides Hash codes are used for hash tables common implementation of collection ADTs see CSE332 libraries won t work if your classes break relevant contracts Cheaper pre-filtering is a more general idea Example: Are two large video files the exact same video? Quick pre-filter: Are the files the same size? CSE331 Summer 2017 33

Doing it So: we have to override hashcode in Duration Must obey contract Aim for non-equals objects usually having different results Correct but expect poor performance: public int hashcode() { return 1; A bit better: public int hashcode() { return min; Better: public int hashcode() { return min ^ sec; Best public int hashcode() { return 60*min+sec; CSE331 Summer 2017 34

Correctness depends on equals Suppose we change the spec for Duration s equals: public boolean equals(object o) { if (!(o instanceof Duration)) return false; Duration d = (Duration) o; return min == d.min && sec/10 == d.sec/10; Must update hashcode why? public int hashcode() { return 6*min+sec/10; CSE331 Summer 2017 35

Equality, mutation, and time If two objects are equal now, will they always be equal? in mathematics, yes in Java, you choose Object contract doesn't specify For immutable objects: abstract value never changes equality should be forever (even if rep changes) For mutable objects, either: use reference equality (never changes) not forever: mutation changes abstract value hence equals Common source of bugs: mutating an object in a data structure CSE331 Summer 2017 36

Examples StringBuilder is mutable and sticks with reference-equality: StringBuilder s1 = new StringBuilder("hello"); StringBuilder s2 = new StringBuilder("hello"); s1.equals(s1); // true s1.equals(s2); // false By contrast: Date d1 = new Date(0); // Jan 1, 1970 00:00:00 GMT Date d2 = new Date(0); d1.equals(d2); // true d2.settime(1); d1.equals(d2); // false CSE331 Summer 2017 37

Behavioral and observational equivalence Two objects are behaviorally equivalent if there is no sequence of operations (excluding ==) that can distinguish them Two objects are observationally equivalent if there is no sequence of observer operations that can distinguish them excludes mutators and == CSE331 Summer 2017 38

Equality and mutation Date class implements (only) observational equality Can violate rep invariant of a Set by mutating after insertion Set<Date> s = new HashSet<Date>(); Date d1 = new Date(0); Date d2 = new Date(1000); s.add(d1); s.add(d2); d2.settime(0); for (Date d : s) { // prints two of same date System.out.println(d); CSE331 Summer 2017 39

Pitfalls of observational equivalence Have to make do with caveats in specs: Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. Same problem applies to keys in maps Same problem applies to mutations that change hash codes when using HashSet or HashMap Especially hard bugs to detect! (Be frightened!) Easy to cause when modules don t list everything they mutate why we need @modifies CSE331 Summer 2017 40

Another container wrinkle: self-containment equals and hashcode on containers are recursive: class ArrayList<E> { public int hashcode() { int code = 1; for (Object o : list) code = 31*code + (o==null? 0 : o.hashcode()); return code; This causes an infinite loop: List<Object> lst = new ArrayList<Object>(); lst.add(lst); lst.hashcode(); CSE331 Summer 2017 41

Summary Different notions of equality: reference equality stronger than behavioral equality stronger than observational equality Java s equals has an elaborate specification, but does not require any one of the above notions also requires consistency with hashcode concepts more general than Java Mutation and/or subtyping make things even less satisfying good reason not to overuse/misuse either CSE331 Summer 2017 42