Re: Writing Classes
KyoGaSuki wrote:
(I should start making an ebook based on all of the help you have
given me so far, thank you all) So -- classes...oh joy...joy...I was
just having trouble with methods...now classes? I look at
examples...I look at them until my brain hurts...I just can't seem to
follow them far. For example, I was looking at an example about a
clock (where you go in and set it, ect)...but then the variable "min"
became "minutes" and then it became "min" again, and I think
eventually it was "min = temp.min" ...I was already confused prior to
that, but that just finished me off ...that, and the comments
definitely didn't help...*defeated* eh, why not -- I guess I will
post the example here anyways. I have a complete arsenal of ebooks on
JAVA at my disposal...but there is so many of them...does anyone have
any suggestions to which explains writing classes the easiest?
example:
The example is not a good one. It has two major flaws
(and maybe others I've missed) and some minor glitches, too.
If you got it from a book somewhere, throw the book away.
If it's your own work -- well, you've got plenty to learn,
but at least you're working to learn it.
/**
* @(#)Clock.java
*
* Clock application
*
* @author
* @version 1.00 2008/4/1
Maybe the date explains some of the example's drawbacks ...
*/
public class Clock {
private int hr; //store hours
private int min; //store min
private int sec; //store sec
//Default constructor
//Postcondition: hr = 0; min = 0; sec = 0
Medium-scale flaw: It's good to write down what a method
or constructor does, but it's even better to write it in doc
comments that can produce Javadoc for the class.
public Clock() {
setTime(0,0,0);
}
//Constructor with parameters, to set the time
//The time is set according t the parameters
//Postcondition: hr = hours; min = minutes; sec = seconds
public Clock(int hours, int minutes, int seconds) {
setTime(hours,minutes, seconds);
}
//Method to set the time
//the time is set according to the parameteres
//Postcondition: hr = hours; min = minutes; sec = seconds
public void setTime(int hours, int minutes, int seconds) {
Serious Flaw Number One: The constructors call this method
while a new Clock object is not yet fully constructed (the
construction isn't finished until the constructor itself is
finished). But this method can be overridden in a subclass
like `CuckooClock extends Clock', and that's a recipe for
trouble. Consider:
When a CuckooClock is constructed, its constructor begins
by calling Clock's constructor (general rule: the superclass
must be constructed before construction of the subclass aspects
starts). But when Clock's constructor calls setTime(), it
will execute CuckooClock's implementation of the setTime()
method, not Clock's own version. That means (1) that Clock's
setTime() might never run at all and the work it should have
done will remain undone, (2) that CuckooClock's setTime()
method gets to see a Clock in an uninitialized state, and (3)
if CuckooClock has instance variables with initializers, any
change CuckooClock's setTime() made to them will get clobbered
after Clock's constructor finishes.
THE RULE: A constructor must never call a method that a
subclass can override.
if(0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if(0 <=minutes && minutes < 60)
min = minutes;
else
min = 0;
if(0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
Lesser flaw: When you discover that a value is out of range,
it might be better to throw an IllegalArgumentException (or
something like it) than to pretend to accept the value but in
fact set the time to something completely unexpected.
}
//Method to return the hours
//Postcondition: The value of hr is returned
public int getHours(){
return hr;
}
//Method to return the minutes
//Postcondition: The value of min is returned.
public int getMinutes() {
return min;
}
//Method to return the seconds
//Postcondition: The value of sec is returned.
public int getSeconds() {
return sec;
}
//Method to print the time
//Postcondition: Time is printed in the form hh:mm:ss
public void printTime() {
if(hr<10)
System.out.print("0");
System.out.print(hr + ":");
if(min < 10)
System.out.print("0");
System.out.print(min + ":");
if(sec <10)
System.out.print("0");
System.out.print(sec);
Lesser flaw: Consider using a NumberFormat object to
handle all that zero-filling. (There's also DateFormat for
times and dates, but that drags in the complications of Date
objects, time zones, localization, and a whole can of worms
you might prefer to put off for another, er, time.)
}
//Method to increment the time by one second
//Postcondition: The time is incremented by one second
//If the before-increment time is 23:59:59, the time
//is reset to 00:00:00.
public void incrementSeconds() {
sec++;
if (sec>59)
{
sec = 0;
incrementMinutes(); //increment minutes
}
}
//Method to increment the time by one minute
//Postcondidtion: The time is incremented by one minute.
//If the before-increment time is 23:59:53, the time
//is reset to 00:00:53.
public void incrementMinutes() {
min++;
if(min>59)
{
min=0;
incrementHours(); //increment hours
}
}
//Method to increment the time by one hour
//Postcondition: The time is incremented by one hour.
//If the before-increment time is 23:45:53, the time
//is reset to 00:45:53.
public void incrementHours() {
hr++;
if(hr>23)
hr=0;
}
//Method to compare the two times
//Postcondition: Returns true if time is equal to
// otherClock; otherwise returns false.
public boolean equals(Clock otherClock) {
Major Flaw Number Two: Although this method does what it
says, it's not "the" equals() method, it's not the method that
a Set, for instance, will use to decide whether two Clocks are
equal. That method takes an Object reference as its argument,
not a Clock reference, and the Set uses it because it doesn't
"know" that it contains only Clocks -- it might contain an
assortment of Clocks, Watches, Rings, Stickpins, and all the
rest of a pawn shop's inventory, so Set needs to be able to
test whether a Clock is equal to a CigaretteCase. If you want
Set to use Clock's idea of equality rather than Object's, you
must implement the equals(Object) method.
Also: If you implement equals(Object), you should also
implement hashCode(), and vice versa. They work as a pair,
or they, well, don't work. The only possible excuse for
implementing one method without the other would be if you
were absolutely certain that objects of the class would
never, ever be used in a hash-based collection -- and since
Clock is a public class, it's impossible that you could
be certain of any such thing.
return (hr==otherClock.hr
&&min == otherClock.min
&&sec == otherClock.sec);
}
//Method to copy the time
//Postcondition: The instance variables of otherClock are
// copied into the corresponding data members
// of this time.
// hr = otherClock.hr; min = otherClock.min;
// sec = otherClock.sec.
public void makeCopy(Clock otherClock){
hr = otherClock.hr;
min = otherClock.min;
sec = otherClock.sec;
Lesser flaw: It would be better to use setTime() here.
Better still, consider renaming this method to setTime(),
so there are two setTime() methods: one that takes three
"random" values, and one that gets them from an existing
Clock object.
}
//Method to return a copy of the time
//Postcondition: A copy of the object is created
// and a reference of the copy is returned.
public Clock getCopy() {
Clock temp= new Clock();
temp.hr = hr;
temp.min = min;
temp.sec = sec;
return temp;
Lesser flaw: You've got a constructor that takes hh,mm,ss
as arguments, so why not use it? Better yet, throw this
method away and write another constructor, one that takes an
existing Clock as its argument.
}
}
--
Eric.Sosman@sun.com