1 Immutable Classes
Total Page:16
File Type:pdf, Size:1020Kb
A Broken Time Period Class Computer Science and Engineering The Ohio State University public class Period { private Date start; Immutable Classes private Date end; Computer Science and Engineering College of Engineering The Ohio State University public Period(Date start, Date end) { assert (start.compareTo(end) > 0); //start < end this.start = start; this.end = end; Lecture 8 } public Date getStart() { return start; } public Date getEnd() { return end; } } Questions Problem: Aliasing Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University What is an invariant in general? Assignment in constructor creates an alias Ans: Client and component both have references to the same Date object Class invariant can be undermined via alias What is an invariant for class Period? Date start = new Date(300); Ans: Date end = new Date (500); Period p = new Period (start, end); end.setTime(100); //modifies internals of p Why is this an invariant for this class? Solution: “defensive copying” Ans: Constructor creates a copy of the arguments Copy is used to initialize the private fields Metaphor: ownership A Better Period Class Good Practice: Copy First Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University public class Period { private Date start; When making a defensive copy of private Date end; constructor arguments: public Period(Date start, Date end) { First copy the arguments assert (start.compareTo(end) > 0); //start < end Then check the validity of the parameters this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); Reason: multithreaded code } Consider a constructor that checks first, public Date getStart() { return start; then copies } Another thread of execution could change public Date getEnd() { the parameters after they pass the validity return end; } check, but before they are copied into the } private fields 1 A Better+1 Period Class Problem: Aliasing Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University public class Period { private Date start; Return value in accessor creates an alias private Date end; Client can still obtain a reference to the class’s internal representation (the private fields) public Period(Date start, Date end) { this.start = new Date(start.getTime()); aka “privacy leak”, but really just an alias problem this.end = new Date(end.getTime()); Class invariant can be undermined via alias assert (this.start.compareTo(this.end) > 0); Date start = new Date(300); } Date end = new Date (500); public Date getStart() { Period p = new Period (start, end); return start; } p.getEnd().setTime(100); //modifies p’s rep public Date getEnd() { Solution: “defensive copying” return end; Accessors create a copy of internal fields } } Copy is returned to the client A Better+2 Period Class Immutability Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University public class Period { private Date start; An immutable object is one whose value can private Date end; not change public Period(Date start, Date end) { Constructor allows initialization to different values this.start = new Date(start.getTime()); No mutator methods this.end = new Date(end.getTime()); ie No alters clauses containing “this” assert (this.start.compareTo(this.end) > 0); } Why would we want such a thing? Because aliasing an immutable is safe! public Date getStart() { return new Date(start.getTime()); Having multiple references to the same immutable } is indistinguishable from having multiple public Date getEnd() { references to different immutables that have the return new Date(end.getTime()); same value } Defensive copies of immutables are not required } How to Write an Immutable Class Examples Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University Do not provide mutators Period Check alters clause of all methods Has fields that refer to mutables (Date) Needs defensive copies Make all fields private String You do that anyway, right? Lots of methods look like they could be mutators Ensure exclusive access to any eg toUpperCase(), substring(int,int), mutable objects referred to by fields replace(char,char) But these methods actually return a String If the class has fields that refer to mutable String str = new String(“Hello there”); objects, make defensive copies in System.out.println(str.toUpperCase()); constructors and for return values from Wrapper classes methods Integer, Long, Float, etc… 2 Good Practice: Immutable Idioms A Better+3 Period Class Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University public final class Period { Declare all fields to be final private final Date start; Guarantees immutability for primitives private final Date end; public Period(Date start, Date end) { For reference type it is no help, but still this.start = new Date(start.getTime()); considered an idiom signaling the intent to this.end = new Date(end.getTime()); write an immutable class assert (this.start.compareTo(this.end) > 0); Declare class to be “final” } public Date getStart() { We will talk about what this qualifier return new Date(start.getTime()); means for classes later } public Date getEnd() { return new Date(end.getTime()); } } Wrapper Classes Boxing and Unboxing Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University Every primitive type has a corresponding Boxing: primitive --> wrapper wrapper class Integer integerObject = new Integer(42); Integer, Long, Float, Double, … Unboxing: wrapper --> primitive The classes are immutable int i = integerObject.intValue(); So no aliasing worries Java does this automatically for you Do not provide a zero-argument constructor Double price = 499.99; //auto-box Do provide useful static constants price = price + 19.90; //auto-unbox then box Integer.MAX_VALUE, Integer.MIN_VALUE But be very careful… Integer i = new Integer(2); Do provide useful static methods Integer j = new Integer(2); Converting from String to primitive: parseInt() assertTrue(i >= j); //success (unboxing) int i = Integer.parseInt(“33342”); assertTrue(i <= j); //success (unboxing) Converting from primitive to String: toString() assertTrue(i == j); //failure! (no unboxing) String str = Double.toString(123.99); Supplemental Reading Summary Computer Science and Engineering The Ohio State University Computer Science and Engineering The Ohio State University Bloch’s “Effective Java” Defensive copying Item 13: Favor Immutability Copy constructor arguments (reference types) Return only copies of fields (reference types) Item 24: Make defensive copies when needed Immutable classes Each instance represents a distinct value No mutators: no methods alter “this” Methods can return a new instance Defensive copying of mutable fields Examples of immutables String Wrapper classes (Integer, Long, Float…) Auto-boxing / auto-unboxing 3.