Puzzle 3 Write the class Enigma which extends

  • Slides: 25
Download presentation
Puzzle 3 Write the class Enigma, which extends Object, so that the following program

Puzzle 3 Write the class Enigma, which extends Object, so that the following program prints false: public class Conundrum { public static void main(String[] args) { Enigma e = new Enigma(); System. out. println( e. equals(e) ); } } You must not override Object. equals() [Java Puzzlers by Joshua Block and Neal Gaffer] 1

Solution the puzzle does not allow you to override equals() but it says nothing

Solution the puzzle does not allow you to override equals() but it says nothing about overloading public class Enigma { public boolean equals(Enigma other) { return false; } } 2

Equality of Instances of Different Types our implementation of equals() is correct in that

Equality of Instances of Different Types our implementation of equals() is correct in that it obeys the equals() contract however, it returns true if and only if two objects have the exact same type 3 because we used get. Class()

Goals for Today implication of get. Class() in equals() implement a simple mutable vector

Goals for Today implication of get. Class() in equals() implement a simple mutable vector class 4 delegating to accessor and mutator methods constructor chaining

Implication of Same Type Equality suppose Phone. Number was not declared final we might

Implication of Same Type Equality suppose Phone. Number was not declared final we might consider extending Phone. Number to keep track of the total number of phone numbers created public class Counted. Phone. Number extends Phone. Number { private static final Atomic. Integer counter = new Atomic. Integer(); // constructor and equals not shown public int number. Created() { return counter. get(); } } // adapted from Effective Java (Item 8) 5

our implementation of equals() says that a Phone. Number is never equal to a

our implementation of equals() says that a Phone. Number is never equal to a Counted. Phone. Number because they have different types // client code somewhere Phone. Number x = new Phone. Number(416, 736, 2100); Counted. Phone. Number y = new Counted. Phone. Number(416, 736, 2100); System. out. println( x. equals(y) ); System. out. println( y. equals(x) ); 6 // false

classes that use equals() might not work the way you want them to for

classes that use equals() might not work the way you want them to for example, Hash. Set uses equals() to organize the way it stores objects // client code somewhere Phone. Number x = new Phone. Number(416, 736, 2100); Counted. Phone. Number y = new Counted. Phone. Number(416, 736, 2100); Hash. Set<Phone. Number> h = new Hash. Set<Phone. Number>(); h. add(y); System. out. println( h. contains(x) ); // false 7

Substituting Types it would be nice if a client could write methods that expect

Substituting Types it would be nice if a client could write methods that expect a Phone. Number but work as expected when given a Counted. Phone. Number after all, a Counted. Phone. Number is a kind of Phone. Number we can as long as we don't use equals() the ability to substitute a subtype (Counted. Phone. Number) for a supertype (Phone. Number) in client code without changing the behaviour of the client code is related to the Liskov Substitution Principle 8

Liskov Substitution Principle often cited as a fundamental principle of objectoriented design (but this

Liskov Substitution Principle often cited as a fundamental principle of objectoriented design (but this is fiercely debated) Let (x) be a property provable about objects x of type T. Then (y) should be true for objects y of type S where S is a subtype of T. observe that by using get. Class() we never get equality (provable property) between Phone. Number objects (objects of type T) and Counted. Phone. Number objects (objects of type S) [don't get hung up on this slide; it's beyond the scope of this course] 9

 use get. Class() when implementing equals() in this course it will satisfy the

use get. Class() when implementing equals() in this course it will satisfy the equals() contract but be aware of its limitation if you need equality between different types: http: //www. angelikalanger. com/Articles/Java. Solutions/Secrets. Of. Equals/Equals. html http: //www. angelikalanger. com/Articles/Java. Solutions/Secrets. Of. Equals/Equals-2. html 10

Mutable Classes 11

Mutable Classes 11

Mutable Classes a mutable class can change how its state appears to clients recall

Mutable Classes a mutable class can change how its state appears to clients recall that immutable classes are generally easier to implement and use so why would we want a mutable class? because you need a separate immutable object for every value you need to represent we will implement a simple class that represents 2 dimensional mathematical vectors 12

What Can Mathematical Vectors Do? add subtract multiply by scalar set components get components

What Can Mathematical Vectors Do? add subtract multiply by scalar set components get components construct equals to. String Vector 2 d - x: double - y: double - name: String + Vector 2 d(): Vector 2 d + Vector 2 d(double, double): Vector 2 d + Vector 2 d(String, double): Vector 2 d + Vector 2 d(Vector 2 d): Vector 2 d + add(Vector 2 d): void + equals(Object): boolean + get. X(): double + get. Y(): double + length(): double + multiply(double): void. . . 13

equals() @Override public boolean equals(Object obj) { boolean eq = false; if (obj ==

equals() @Override public boolean equals(Object obj) { boolean eq = false; if (obj == this) { eq = true; } return eq; } 14

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this)

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this) { eq = true; } else if (obj != null && this. get. Class() == obj. get. Class()) { } return eq; } 15

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this)

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this) { eq = true; } else if (obj != null && this. get. Class() == obj. get. Class()) { Vector 2 d v = (Vector 2 d) obj; } return eq; } 16

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this)

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this) { eq = true; } else if (obj != null && this. get. Class() == obj. get. Class()) { Vector 2 d v = (Vector 2 d) obj; if (this. get. Name() == null && v. get. Name() != null) { eq = false; } } return eq; } 17

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this)

@Override public boolean equals(Object obj) { boolean eq = false; if (obj == this) { eq = true; } else if (obj != null && this. get. Class() == obj. get. Class()) { Vector 2 d v = (Vector 2 d) obj; if (this. get. Name() == null && v. get. Name() != null) { eq = false; } else { eq = this. get. Name(). equals( v. get. Name() ) && Double. compare( this. get. X(), v. get. X() ) == 0 && Double. compare( this. get. Y(), v. get. Y() ) == 0; } } return eq; } 18

Observe That. . . instead of directly using the attributes, we use accessor methods

Observe That. . . instead of directly using the attributes, we use accessor methods where possible this reduces code duplication, especially if accessing an attribute requires a lot of code this gives us the possibility to change the representation of the attributes in the future as long as we update the accessor methods (but we would have to do that anyway to preserve the API) for example, instead of two attributes x and y, we might want to use an array or some sort of Container the notes [notes 2. 3. 1] call this delegating to accessors 19

set. X(), set. Y(), and set() public void set. X(double x) { this. x

set. X(), set. Y(), and set() public void set. X(double x) { this. x = x; } public void set. Y(double y) { this. y = y; } public void set(double x, double y) { this. x = x; this. set. X(x); this. y = y; delegate to mutator this. set. Y(y); } public void set(String name, double x, double y) { this. name = name; } 20 this. x = x; this. y = y; this. set(x, y); delegate to mutator

Observe That. . . instead of directly modifying the attributes, we use mutator methods

Observe That. . . instead of directly modifying the attributes, we use mutator methods where possible this reduces code duplication, especially if modifying an attribute requires a lot of code this gives us the possibility to change the representation of the attributes in the future as long as we update the mutator methods (but we would have to do that anyway to preserve the API) for example, instead of two attributes x and y, we might want to use an array or some sort of Container the notes [notes 2. 3. 1] call this delegating to mutators 21

Constructors public Vector 2 d(String name, double x, double y) { this. name =

Constructors public Vector 2 d(String name, double x, double y) { this. name = name; this. x = x; delegate to mutator this. y = y; this. set(name, x, y); } public Vector 2 d(Vector 2 d v) { this. name = v. name; this. x = v. x; delegate to constructor and accessors this. y = v. y; this(v. get. Name(), v. get. X(), v. get. Y()); } 22

More Constructors public Vector 2 d(double x, double y) { this(null, x, y); good,

More Constructors public Vector 2 d(double x, double y) { this(null, x, y); good, delegate to constructor } public Vector 2 d() { this(null, 0. 0); good, delegate to constructor } 23

Observe That. . . all of the simpler constructors do nothing except call the

Observe That. . . all of the simpler constructors do nothing except call the most general constructor again, this reduces code duplication notes [notes 2. 3. 1] call this constructor chaining the most general constructor calls a mutator 24 again, this reduces code duplication

Things to Think About how do you implement Vector 2 d using an array

Things to Think About how do you implement Vector 2 d using an array to store the coordinates? how do you implement Vector 2 d using a Container to store the coordinates? how do you implement Vector. Nd, an N-dimensional vector? 25