Object and Reference Immutability using Java Generics Yoav Zibin(1), Alex Potanin(2), Shay Artzi(1), Adam Kiezun(1), and Michael D. Ernst(1) 1) MIT Computer Science and Artificial Intelligence Lab, USA 2) Victoria University of Wellington, New-Zealand Presenting: Ori Arad –
2/23 Immutability – What for?... Program comprehension Verification Compile & Run-time optimizations Invariant detection Refactoring Test input generation Regression oracle creation Specification mining Modeling
3/23 Immutability varieties Class immutability No instance of an immutable class may be change after creation (e.g. String, Integer etc.) Object immutability Immutable instances may not be changed, while other instances (of the same class) may… Reference immutability a given reference cannot be used to modify its referent.
4/23 IGJ - Immutability Generic Java Object immutability: An object: mutable or immutable. Reference immutability: A reference: Immutable, Mutable, or ReadOnly Class immutability Also support Class immutability
5/23 IGJ - Immutability Generic Java one new generic parameter (at the beginning of the list of generic parameters) was added 1: // An immutable reference to an immutable date; side effects are prohibited 2: Date immutD = new Date (); 3: // A mutable reference to a mutable date; side effects are permitted 4: Date mutD = new Date (); 5: // A readonly reference to any date; side effects are prohibited 6: Date roD =... ? immutD : mutD; 1: // An immutable reference to an immutable date; side effects are prohibited 2: Date immutD = new Date (); 3: // A mutable reference to a mutable date; side effects are permitted 4: Date mutD = new Date (); 5: // A readonly reference to any date; side effects are prohibited 6: Date roD =... ? immutD : mutD;
6/23 IGJ main design principles Transitivity: The design must provide transitive (deep) immutability Purely static: There should be no runtime representation for immutability Polymorphism: It must be possible to abstract over immutability without code duplication Simplicity: The design should not change Java's syntax and have a small set of typing rules
7/23 Type Hierarchies for IGJ The type hierarchy for immutability parameters The top of the type hierarchy for all other classes
8/23 IGJ Subtype hierarchy // Demonstrates how to copy value of readonly roObj to mutable mutableObj Object roObj =...; List > l = new List >(); ((List >) l).add(roObj); Object mutableObj = l.get(0); // mutableObj equals roObj // Demonstrates how to copy value of readonly roObj to mutable mutableObj Object roObj =...; List > l = new List >(); ((List >) l).add(roObj); Object mutableObj = l.get(0); // mutableObj equals roObj Legend: L List, O object, R ReadOnly IM Immutable, M Mutable For example: L > means List >
9/23 Java Array Co-Variance Problem Read from Array: OK! Write to Array: Problem! // A is a single-element array of String. String[] a = new String[1]; // B is an array of Object Object[] b = a; // Assign an Integer to b. This would be possible if b really were // an array of Object, but since it really is an array of String, // we will get a java.lang.ArrayStoreException. b[0] = new Integer (1); // A is a single-element array of String. String[] a = new String[1]; // B is an array of Object Object[] b = a; // Assign an Integer to b. This would be possible if b really were // an array of Object, but since it really is an array of String, // we will get a java.lang.ArrayStoreException. b[0] = new Integer (1); Solution with IGJ – immutability promise us only reading no Co-Variance problem
10/23 The Field Rule legal iff I(o) = Mutable. Example : Employee roE =...; roE.address =...; // Compilation error! Employee roE =...; roE.address =...; // Compilation error! o.someField = exp; Typing is guaranteed by several rules. Let I(x) denote the immutability of x Rule I: Field Assignment Rule
11/23 "Immutability" of Methods 4 @Immutable void m() {...this...} Here: I( this) is Mutable In general: in any method m, I( this ) is the same as I( m ) “ this ” immutability depends on the context
12/23 Reference-Immutability Rules Method Invocation Rule: (not necessarily I(o) = I(m) ) Employee o =...; o.setAddress(...); // OK since I(o) = Mutable and I(setAddress) = Mutable o.getAddress(); // OK since I(o) = Mutable and I(getAddress) = ReadOnly ((Employee ) o).setAddress(...); // Compilation error! Employee o =...; o.setAddress(...); // OK since I(o) = Mutable and I(setAddress) = Mutable o.getAddress(); // OK since I(o) = Mutable and I(getAddress) = ReadOnly ((Employee ) o).setAddress(...); // Compilation error! o.m(...) is legal if I(o) is subtype of I(m).
13/23 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id; } 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: } 10: class Graph { 11: public Edge lastN; 12: public List > l; 13: Graph(List > l) { this.l = l; } 14: void addEdge(Edge n) { 15: this.l.add(n); this.lastN = n; } 16: Edge getLast() { return this.lastN; } 17: public static 18: Edge findEdge(Graph nl, long id) {... } 19: } 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id; } 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: } 10: class Graph { 11: public Edge lastN; 12: public List > l; 13: Graph(List > l) { this.l = l; } 14: void addEdge(Edge n) { 15: this.l.add(n); this.lastN = n; } 16: Edge getLast() { return this.lastN; } 17: public static 18: Edge findEdge(Graph nl, long id) {... } 19: } Example IGJ classes Edge and Graph, with the immutability parameters (and annotations, for this) underlined. For now: is is
14/23 Example: Line 5: the assignment into this.id is OK If this.id = …; was on line 6 – illegal… Line 3: OK due to Method Rule Line 8: static no annotation, Edge of any immutability could be pass here… 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id; } 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: } 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id; } 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: }
15/23 Example: Line 11: a class can pass its immutability parameter to its fields Line 16: Also return type – no need for overloading Line 12: Transitivity - in an immutable Graph the field l will contain an immutable list of immutable edges 10: class Graph { 11: public Edge lastN; 12: public List > l; 13: Graph(List > l) { this.l = l; } 14: void addEdge(Edge n) { 15: this.l.add(n); this.lastN = n; } 16: Edge getLast() { return this.lastN; } 17: public static 18: Edge findEdge(Graph nl, long id) {... } 19: } 10: class Graph { 11: public Edge lastN; 12: public List > l; 13: Graph(List > l) { this.l = l; } 14: void addEdge(Edge n) { 15: this.l.add(n); this.lastN = n; } 16: Edge getLast() { return this.lastN; } 17: public static 18: Edge findEdge(Graph nl, long id) {... } 19: }
16/23 Object Immutability & constructors What annotation should CTOR problem: Graph(List > l) { this.l = l; this.addEdge( new Edge (0) ); // } … List > imList =...; new Graph (imList); // An element was added to imList Graph(List > l) { this.l = l; this.addEdge( new Edge (0) ); // } … List > imList =...; new Graph (imList); // An element was added to Guess not…
17/23 Object Immutability & constructors Solution: Forth kind of reference immutability: AssignFields Permit to perform limited side-effects without permitting modification of immutable method can assign & method can only assign
18/23 Immutability & Assignability MyClass myObject = new MyClass(); myObject = anotherObject; Assignability myObject.setField(4); immutability
19/23 Revised rules Field Rule revised (relaxed): AssignField is not transitive… Method Rule revised (restricted): e.g. this.l.get(0).setId(42); // will be illegal o.someField =...; is legal if I(o) = Mutable or (I(o) = AssignFields and o=this) o.m(...) is legal if I(o) is subtype of I(m) and (if I(m) is AssignFields o must be this)
20/23 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id; } 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: } 10: class Graph { 11: public Edge lastN; 12: public List > l; 13: Graph(List > l) { this.l = l; } 14: void addEdge(Edge n) { 15: this.l.add(n); this.lastN = n; } 16: Edge getLast() { return this.lastN; } 17: public static 18: Edge findEdge(Graph nl, long id) {... } 19: } 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id; } 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: } 10: class Graph { 11: public Edge lastN; 12: public List > l; 13: Graph(List > l) { this.l = l; } 14: void addEdge(Edge n) { 15: this.l.add(n); this.lastN = n; } 16: Edge getLast() { return this.lastN; } 17: public static 18: Edge findEdge(Graph nl, long id) {... } 19: } Example The original code –
21/23 Example: Line 5: the assignment into this.id is still OK setId is annotated I(this)=AssignFields Line 3: I(this) = AssignFields & I(setId) = AssignFields 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id;} 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: } 1: class Edge { 2: private long id; 3: Edge(long id) { this.setId(id); } 4: synchronized void setId(long id) { 5: this.id = id; } 6: synchronized long getId() { return id;} 7: long getIdImmutable() { return id; } 8: public static void print(Edge n) {... } 9: }
22/23 Example: Line 13: If we will add the following code – it will be illegal in the revised rule (and legal in the old one) Graph(List > l) { this.l = l; this.l.get(0).setId(42); // } Graph(List > l) { this.l = l; this.l.get(0).setId(42); // } New Rule: new SomeClass (…) is legal only if X = Mutable and this CTOR is not marked or X = Imuutable and this CTOR is not marked (X = readOnly & X=AssignFileds are illegal)
23/23 Future Work Plug-in for IDE (e.g. Eclipse) A WriteOnly immutability notation for field class Vector int int size() { return size; void add(T t) {... void removeLast() {... void remove(T t) {... void get(int index) {... } } class Vector int int size() { return size; void add(T t) {... void removeLast() {... void remove(T t) {... void get(int index) {... } }
24/23 Future Work (cont.) Add default immutability class Graph An alternative syntax (Java 7?...) Runtime support (e.g. ArrayList ArrayList (...)
25/23 Questions?