
In today's lecture we review the concepts of
reference equality, object equality, accessors,
and mutators. We also look at the consequences
of badly designed class, and exactly what
static means for attributes of a class.
Accessor methods let the client ask for information about the object's state. An important feature of an accessor method is that invoking it does not change the state of the object.
A Fraction object has a numerator and a
denominator. The client can ask for the values of the
numerator, denominator, and separator using accessor methods.
import java.io.PrintStream;
import type.lib.Fraction;
public class AccessorExample1
{
public static void main(String[] args)
{
PrintStream output = System.out;
Fraction f = new Fraction(3, 5);
long numer = f.getNumerator();
long denom = f.getDenominator();
char sep = f.getSeparator();
output.printf("numer: %d denom: %d sep: %c%n",
numer, denom, sep);
}
}
The name of an accessor often starts with get, but
not every method with a name that starts with get
is an accessor.
If the accessor returns a boolean value then its name usually
starts with is.
Some objects allow the client to change the state of the object. Methods that change the state of the object are called mutators.
The Fraction class allows the client to change
the values of the numerator and denominator.
import java.io.PrintStream;
import type.lib.Fraction;
public class MutatorExample1
{
public static void main(String[] args)
{
PrintStream output = System.out;
Fraction f = new Fraction(3, 5);
output.printf("f is %s%n", f);
f.setNumerator(17);
output.printf("f is now %s%n", f);
f.setDenominator(11);
output.printf("f is now %s%n", f);
f.setFraction(4, 9);
output.printf("f is now %s%n", f);
}
}
Allowing a client to change the state of an object has serious implications because of the possibility that the client may (unknowingly or maliciously) put the object into an invalid state. Examples (all for illustration puposes only):
Person to -1BankAccount holdsTorontoMapleLeafs to make the playoffsThe contract of a mutator method states what happens when a mutator method is invoked.
A mutator method contract with a precondition places all responsibility for invalid arguments on the client (as you already know).
If the client passes an invalid argument to such a method then anything can happen.
No Java Standard Library method uses this approach.
boolean Return
A mutator method contract can indicate that the method
returns a boolean value to indicate if
the method invocation was successful.
Consider the type.lib.Fraction method
setSeparator:
public boolean setSeparator(char newSeparator)
A mutator of the separator of this fraction. The separator must
not be a letter or a digit.
Parameters:
newSeparator - the new separator character.
Returns:
true if the change was made (i.e. if the passed
parameter is neither a letter nor a digit), and return
false otherwise.
The client is responsible for checking the return value to ensure that the method was invoked successfully.
A mutator method contract can indicate that the method throws an exception if the client passes an invalid argument to the method.
The class java.lang.StringBuilder is very
similar to java.lang.String except that
StringBuilder has mutator methods and
String does not. Consider its method
setLength:
public void setLength(int newLength)
Sets the length of the character sequence...
The newLength argument must be greater
than or equal to 0.
Parameters:
newLength - the new length.
Throws:
IndexOutOfBoundsException if the
newLength argument is negative.
There is no way for a client to ignore an exception.
An accessor is a method that lets the client read the value of an attribute.
A mutator is a method that lets the client write the value of an attribute.
By providing accessor and mutator methods, the class
implementer can hide all of the attributes from the client
(make them private). From the client's point
of view, this is a good thing because the client can use
the class without knowing the details of how it is implemented
(as long as the method contracts are obeyed).
Suppose you have two reference variables:
Fraction f; Fraction g; /* Some code that assigns values to f and g... */
Then
boolean sameObject = (f == g);
is true if and only
f and g refer to the same object.
boolean differentButSimilarObject = f.equals(g);
is true if and only if the state of the objects
referred to by f and g
are the same (as defined by the implementer of the class).
What does the following code fragment output?
Fraction f = new Fraction(1, 2); Fraction g = new Fraction(3, 4); output.println(f.getNumerator()); output.println(f == g); output.println(f.equals(g));
What line of code would you need to add to:
Fraction f = new Fraction(1, 2); Fraction g = new Fraction(3, 4); // your line of code here output.println(f.getNumerator()); output.println(f == g); output.println(f.equals(g));
to produce the following output?
3 true true
What line of code would you need to add to:
Fraction f = new Fraction(1, 2); Fraction g = new Fraction(3, 4); // your line of code here output.println(f.getNumerator()); output.println(f == g); output.println(f.equals(g));
to produce the following output?
3 false true
What line of code would you need to add to:
Fraction f = new Fraction(1, 2); Fraction g = new Fraction(3, 4); // your line of code here output.println(f.getNumerator()); output.println(f == g); output.println(f.equals(g));
to produce the following output?
1 false true
What line of code would you need to add to:
Fraction f = new Fraction(1, 2); Fraction g = new Fraction(3, 4); // your line of code here output.println(f.getNumerator()); output.println(f == g); output.println(f.equals(g));
to produce the following output?
6 false false
public Attributes
public char separator
A character that separates the numerator denominator pair
in the return of the toString() method.
The default value is '/'.
import type.lib.Fraction;
public class SeparatorExample1
{
public static void main(String[] args)
{
Fraction f = new Fraction();
System.out.println(f.separator);
System.out.println(f);
f.separator = ':';
System.out.println(f.separator);
System.out.println(f);
}
}
public AttributesBecause the attribute is public, the client
can change the attribute value to something inappropriate
(i.e. the client can put the object into an invalid state).
import type.lib.Fraction;
public class SeparatorExample2
{
public static void main(String[] args)
{
Fraction f = new Fraction();
f.separator = '0';
System.out.println(f.separator);
System.out.println(f);
}
}
public AttributesThe Fraction class was intentionally implemented
with a public attribute to illustrate the fact that
public attributes allow a client to easily put an
object into an invalid state.
If the client had used the setSeparator mutator,
the Fraction object could have prevented itself from
being put into an invalid state.
import type.lib.Fraction;
public class SeparatorExample3
{
public static void main(String[] args)
{
Fraction f = new Fraction();
boolean separatorOK = f.setSeparator('0');
System.out.println(separatorOK);
System.out.println(f.separator);
System.out.println(f);
}
}
The advantage of forcing the client to use a mutator method is that the method has a contract whereas an attribute can never have a contract.
static AttributesIf a class has a static attribute, then
the class is responsible for storing the attribute (i.e.
no object has its own version of the attribute).
public static boolean isQuoted
A flag that determines if the return of the
toProperString() method is surrounded by
quotes or not.
The default value is true.
import java.io.PrintStream;
import type.lib.Fraction;
public class StaticExample1
{
public static void main(String[] args)
{
PrintStream output = System.out;
Fraction f = new Fraction(25, 8);
Fraction g = new Fraction(100, 1);
output.println(Fraction.isQuoted);
output.println(f.toProperString());
output.println(g.toProperString());
output.println();
Fraction.isQuoted = false;
output.println(Fraction.isQuoted);
output.println(f.toProperString());
output.println(g.toProperString());
}
}
static Attributes Memory DiagramNotice that the class stores the
static attribute isQuoted whereas
the object stores the values for the numerator and
denominator.
| 0 | ||
| ¦ | ||
| main | ||
| f ⇒ | 100 | 600 |
| g ⇒ | 120 | 700 |
| ¦ | ||
| 500 | Fraction class | |
| numerator ⇒ | 504 | |
| denominator ⇒ | 520 | |
| isQuoted ⇒ | 524 | true |
| ¦ | ||
| 600 | Fraction object | |
| numerator ⇒ | 604 | 25 |
| denominator ⇒ | 620 | 8 |
| ¦ | ||
| 700 | Fraction object | |
| numerator ⇒ | 704 | 100 |
| denominator ⇒ | 720 | 1 |
When the client sets the value of isQuoted to
false the value stored at address 524 is changed
to false.
See the discussion in Section 4.3.3 of the textbook.