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.
Suppose you have two references to unique objects:
Fraction f = new Fraction(1, 2); Fraction g = new Fraction(3, 4);
The statement:
boolean sameObject = (f == g);
is true
if and only if the references
f
and g
refer to the same object.
The statement:
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
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 true false
An accessor is a method that lets the client read the value of an attribute. More generally, an accessor performs some action using the attributes of the object but does not modify the values of the attributes.
A mutator is a method that lets the client write the value of an attribute. More generally, a mutator performs some action that modifies the attributes of the object.
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.
public
AttributesThe Fraction
class has a public
attribute named separator
that stores the value
of the character used to separate the numerator and
denominator in the toString
method.
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 can do something if the client tries to put the object into an invalid state:
static
AttributesIf a class has a static
attribute, then
the class is responsible for storing the attribute (i.e.
each object of the class does not have its own version of
the attribute).
The Fraction
class has a
public static
attribute that controls how each
Fraction
object computes the textual
representation of itself as a proper fraction.
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); output.println(Fraction.isQuoted); output.println(f.toProperString()); output.println(); Fraction.isQuoted = false; output.println(Fraction.isQuoted); output.println(f.toProperString()); } }
static
Attributes Memory DiagramBelow is the memory diagram for the previous example up to
the point just after the Fraction
object is printed
the first time. Notice that the class stores the
static
attribute isQuoted
whereas
the object stores the values for the numerator and
denominator.
0 | ||
¦ | ||
main | ||
f ⇒ | 100 | 600 |
¦ | ||
500 | Fraction class | |
numerator ⇒ | 504 | |
denominator ⇒ | 520 | |
isQuoted ⇒ | 524 | true |
¦ | ||
600 | Fraction object | |
numerator ⇒ | 604 | 25 |
denominator ⇒ | 620 | 8 |
¦ |
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.