Counter
class that we saw in lecture. (The version in which you can specify
how many digits you want to print.)
Observe that, for a given Counter object, whenever the
show method is called on that object, it creates the same
format string and a DecimalFormat object that does the
same thing every time. In other words, the way I designed this
version of the Counter class, the show
method does repeated work each time it's called.
Instead of creating the format string and the
DecimalFormat object each time that show is
called, your first mission is to alter the Counter class
so that each constructor creates an appropriate
DecimalFormat object once and for all, saving a reference
to this DecimalFormat object in an instance variable of
Counter. To accomplish this change, you need to do the
following:
myDigits, since the
DecimalFormat object that each constructor creates
obviates the need to keep track of the number of digits to
display. (In other words, the number of digits to display is
already taken into account in the DecimalFormat
object.)
DecimalFormat object, and give this variable an
appropriate name.
createFormat. This method takes one parameter, an
int giving the number of digits to display for the
Counter, and it returns a reference to a
DecimalFormat object. This method should start
off much like the show method that we saw in
class. That is, given the number of digits to display (but
here, provided as the parameter, rather than as an instance
variable), it creates a format string, and then it creates a
DecimalFormat object, passing the format string to
the DecimalFormat constructor. The
createFormat method finishes up by simply
returning to its caller a reference to the
DecimalFormat object it created.
Although createFormat will be a private method of
Counter, it need not, and should not, access any
instance variables of the Counter class.
createFormat, where each constructor stores the
reference to the DecimalFormat object returned by
createFormat into the instance variable that you
added in step 2, above.
For the first two constructors, the number of digits you pass
to createFormat should just be the value that a
call to the private method digitsToDisplay
returns. (Of course, make sure that any call to
digitsToDisplay occurs after assigning to
the instance variable myLimit, since
digitsToDisplay relies on myLimit
having been previously set.)
For the third constructor, the number of digits you pass to
createFormat should be the same as I stored in the
instance variable myDigits. As in my version of
this constructor, you should print out the warning that I
printed out when too few digits are requested.
Counter class is
more efficient than mine, because now it doesn't do the same work over
and over upon each call of show.
You're not done, however. I want you to make some more changes.
You might recall that if one operand of the + operator is
a reference to a String but the other operand is not,
then the other operand is converted to a reference to a
String. How does this happen? It turns out that
every class in Java has a method named toString,
which is called implicitly if necessary. Java provides a default
version of toString for all classes: you get a reference
to a String containing the object's address in memory,
written in hexadecimal (i.e., base 16). That might not seem terribly
useful to you, but believe it or not, it can be.
But you always have the option of defining your own
toString method for a class. That's what you'll do for
Counter.
toString, that takes
no parameters and returns a reference to a String.
The String should be the same String
that my version of show prints by passing it to
System.out.print.
How do you create this String? You've got a
reference to a DecimalFormat object saved in an
instance variable, and you know that when you call the
format method of DecimalFormat, it
returns a reference to a String. So that's all
toString has to do: call the format
method of the DecimalFormat object whose reference
is stored in the instance variable, and return the
String reference that format
returned. Naturally, the parameter passed to
format should be the current value of the
Counter.
show method did to the toString
method. The show method no longer has to create a
format string or a DecimalFormat object, because
the constructors do that, and it no longer has to call the
format method, because toString does
that.
Therefore, you should modify the show method so
that it merely prints to the console the String
whose reference is returned by a call of toString.
If this sounds incredibly simple, that's because it is.
Counter class:
you're going to add another constructor. Recall how if
s1 and s2 are references to
String objects, then s2 = new String(s1)
makes a copy of the String that s1
references and assigns to s2 a reference to this new
String. You're going to create similar behavior for
Counter objects.
Counter. For the sake of making this
concrete, the header of this constructor should be
other references a Counter,
which is the Counter whose instance variables you
are to copy into the instance variables of the
Counter you're constructing.
The body of this constructor is really simple: three assignment
statements, one for each instance variable. Not surprisingly,
you can just use myLimit, myValue,
and the instance variable you added in step 2 above on the
left-hand sides of the assignment statements. And you should
use the corresponding instance variables of the
Counter referenced by other on the
right-hand sides.
How can you access the instance variables of the
Counter referenced by other, when
it's not the object that the constructor is called on?
Actually, it turns out to be really easy. One of the rules of
Java is that
If a method in a class has a reference to an object of that class, then the method can access all the instance variables of that object, even if the object is not the one that the method was called on, and even if the instance variables are private.
You access an instance variable using a syntax
similar to a method call:
objectReference.instanceVariable, but of course
with no parentheses needed, since you're not calling a
method.
So, within your constructor, you can access the instance
variable myLimit of the object referenced by
other by simply writing
other.myLimit.
Bozo with a private instance variable
shoeSize, and if a method of Counter had a
reference clown to a Bozo object, then we
cannot say clown.shoeSize within methods of
Counter.
Once you have made the above changes, you need to modify the driver to
show that they work. Fortunately, you don't need to do much for the
first three constructors, toString, or show,
since the driver that I wrote already exercises them. (It exercises
toString because it calls show, which will
call toString when you complete step 6 above.) But you
should modify the driver to show that the new constructor you wrote,
which makes a copy of a Counter, works. All you need to
do is make a copy of a Counter, and call
tick on the copy a different number of times from how
many times you call tick on the original
Counter, and then call show on both of them.
(Hint: You can call tick on one of the
Counter objects zero times.)
To prove that toString is called implicitly, I also want
you to add to the driver a call of System.out.print,
where the parameter is just a reference to a Counter
object. You should get exactly the same output to the console as if
you had called show on this object.
Hand in your modified Counter and
CounterDriver classes, along with the output from running
the program. Start with my Counter
and CounterDriver
classes, and modify them. Don't change the names of these classes.
You don't have to hand in the following, but it's worth trying.
Observe that once you assign to the instance variables
myLimit and the reference to the
DecimalFormat object that you add, their values will
never change. What would happen if you declared them as not only
private, but also as final? Try it; it'll take you only a minute.