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 |
f ⇒ | 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.