//Date.java //example of a class with lots of features. //Class is a template or pattern, a data type. //Object is an actual instance of a class, ie. an instantiated variable of a class. //Class/object has members: methods and data. //members can be public or private. //Usually the data is private and the methods are public. //Public methods are the interface, the "messages" sent to the object. //Private data members are hidden, inaccessible to client code. //Each object has its own set of data members and conceptually its own set of methods. // . operator to access public members. //The methods process the data. They do so correctly and securely. //Client code not trustworthy enough to access the data, nor should it be //burdened to have to do so. Also, implementation changes won't affect client code. //Object knows how to process itself. //public class means is usable by any other code. public class Date { //********** static variables *************** //static variable ("class variable") is per class, not per object. //one for the entire class. each object does not have its own copy. private static int numberDates=0; //common example is an object counter variable, //incremented in each constructor. //private so only this class can directly access it. //final is a constant. can not be changed. //conventional to be in all caps and use _ //accessed thru class name: Date.VALID_START_YEAR //examples: Math.PI Integer.MAX_VALUE public static final int VALID_START_YEAR=1584; //********** instance variables ************* //"instance variables" / "fields": //each object has it own set of instance variables. //(variable of a class type is an "object"). //private access modifier means not directly accessible by client code. //if not static, is instance variable: each object has own copy. private int year; private int month; private int day; //********** constructors ******************* //"constructor". method with same name as class. //constructor has no return type and no return statement. //called automatically when creating/instantiating Date object. //public means callable by client code. //purpose is to correctly initialize object. //can have multiple "overloaded" constructors. //differ by and are differentiated by parameter list. //as a convenience to client. //"no-arg" (0 parameters) constructor. //this one sets date to Java/Unix "epoch" of 1/1/1970 public Date() { month = 1; day = 1; year = 1970; numberDates++; // Date.numberDates++; } //a constructor with 3 int arguments representing the month, day and year that //this Date object should be initialized to public Date(int newMonth, int newDay, int newYear) { month = newMonth; day = newDay; year = newYear; numberDates++; } //"copy" constructor. parameter is another object of same class. //makes this object a copy of the other. //can access private members of other objects of the same class. public Date( Date other) { month = other.month; day = other.day; year = other.year; numberDates++; } //another possibility is a constructor with a String parameter in the // mm/dd/yyyy form. (mm as 1-12) //Extract the 3 values using a StringTokenizer public Date( String date) { java.util.StringTokenizer st = new java.util.StringTokenizer(date,"/"); month = Integer.parseInt(st.nextToken()); //extract next token day = Integer.parseInt(st.nextToken()); year = Integer.parseInt(st.nextToken()); numberDates++; } //********** accessor methods *************** //"getter"/"accessor"/"observer" method to safely access instance variable //or some property of the object. public int getYear() { return year; } public int getMonth() { return month; } public int getDay() { return day; } //conventional to have a toString method that returns a string version //of the object for display purposes. Called automatically in contexts //where a String is expected, like println(""+date1Obj); //"spill your guts": spit out object's state, i.e. instance variables. //Without this method the default display string is classname@hashcode, //e.g. Date@4d9a (not very useful) public String toString() { return(month + "/" + day + "/" + year); } //static method is per class. each object does not have own copy. //can only access static variables because there are no instance variables //because there is no object. //accessed thru class name: Date.getNumberDates() public static int getNumberDates() { return numberDates; } //********** mutator methods *************** //"setter"/"mutator" method is one that changes the object. public void increment() { day++; //stub for the actual method to be written later... } public static void clearNumberDates() { numberDates = 0; } //static method can provide a service/utility. not associated with any object. //Examples: Math.sqrt JOptionPane.showInputDialog Integer.parseInt public static boolean isValidDay(int month, int day, int year) { //bunch of code will go here to determine if month day year //combination is valid or not... return true; //or false, just to chill out the compiler for this example } //you define what "equality" means for a class. //for Dates, it makes sense that two dates are equal if their day, month, and year //are the same, so that is what is being done here. //You might, however, decide that dates are equal if only their month and day are //the same. In that case this equals would need changing. //In general, for a class, it might be a subset of the instance variables //that determines equality. //Note: == only tests if two object references have the same value, i.e. //they are references to the same object (i.e. they are aliases). // == does not and can not look inside the object at the instance variables. //so to test if two objects are "equal", a method must be made. //Read this commented code, it would suffice as a simple equals method: /* public boolean equals( Date other ) { if (day==other.day && month==other.month && year==other.year) return true; else return false; } */ //BUT: this is not what an "official" equals method should be. //It requires complicated stuff including inheritance, //overriding, instanceof, casting, check for null. //In order for this Date class to be somewhat complete, here follows a good equals: //override equals() of Object. //used to determine if two objects (of Date class) are "equal". //MUST be overridden to use Date objects (uniquely, as keys) in a HashSet. //equality is defined by this method. //without this method, two objects are equal only if their references are the same. //must be this signature: parameter MUST be type Object. public boolean equals( Object other ) { //parameter will be the other Date //check that other really is Date, //then (safely) downcast to Date to access instance variables if ((other instanceof Date) && ((((Date)other).year==year) && (((Date)other).month==month) && (((Date)other).day==day))) return true; else return false; //if other is null, or not a Date, or doesn't equal } //equals must be //1. reflexive: x.equals(x) is true //2. symmetric: x.equals(y) == y.equals(x) //3. transitive: if x.equals(y) and y.equals(z) then x.equals(z) //also: x.equals(null) is false //override hashCode() of Object //if two Dates are equal, they must have same hashcode. //if equals is overridden, then hashcode must be overridden too. //must be this signature: public int hashCode() { //return 1492; //would work but inefficient //?? how to make good hash?? //some function of the instance variables modded by some prime... return (year + month + day) % 101; } //compareTo methods return negative if this object is "less than" parameter object, //positive if this object is "greater than" parameter object, //0 if this object is "equal to" parameter object. //You define what equal, less than, and greater than mean for a class. public int compareTo( Date other ) { if (equals(other)) //use the equals method. or can do: this.equals(other) return 0; //not the same, so see if years differ: else if (year < other.year) return -1; else if (year > other.year) return 1; //years are same, so see if months differ: else if (month < other.month) return -1; else if (month > other.month) return 1; //months are same too, so see how days differ: else if (day < other.day) return -1; else if (day > other.day) return 1; else //this else is not needed logically, but without it the compiler complains :( return 0; //will never be reached. } } /* example client code: Date d0 = new Date(5,15,1998); //3 int constructor Date d1 = new Date(); //no-arg constructor Date d2 = new Date("12/23/1999"); //String parameter constructor Date d3 = new Date(d2); //copy constructor, d2 is parameter to be copied. */ /* client code: if (myYear < Date.VALID_START_YEAR) System.out.println("Years before "+Date.VALID_START_YEAR+" are no good"); //each Date object created increments the numberDates variable. //client can learn its value: System.out.println("Number of Date objects created:"+Date.getNumberDates()); //reset the application. dispose of all date objects, then: Date.clearNumberDates(); //use the valid day combo checker: if (Date.isValidDay(m,d,y))... */ /* client code: Date d1 = new Date(12,19,2000); Date d2 = new Date(); // 1/1/1970 Date d3 = new Date(d1); if (d1.equals(d3)) // true because d3 is copy of d1 via copy ctor System.out.println("dates are equal"); if (!d1.equals(d2)) System.out.println("dates are not equal"); if (d1.compareTo(d2) < 0) //false, d1 is not < d2 if (d1.compareTo(d2) > 0) //true, d1 is > d2 if (d1.compareTo(d3) == 0) //true, d1 is = d3 Might as well use equals() */