Search code examples
javagregorian-calendar

When trying to create a new date by adding 4 years to an established date, both dates are changed (GregorianCalendar)


Full disclosure, this is homework, but only a very small part of an assignment that I can't figure out why it's not working. Here is the assignment text:

Create a CollegeStudent class. This class will contain data fields that hold a student's first name, last name, enrollment date, and projected graduation date, using the GregorianCalendar class for each date. Provide get() and set() methods for each field. Also provide a constructor that requires first and last names and enrollment date, and sets the project graduation date to exactly four years after enrolment. Save the class as CollegeStudent.java.

Create an interactive application that prompts the user for data for two CollegeStudent objects. Prompt the user for first name, last name, enrollment month, enrollment day, and enrollment year for each CollegeStudent, and then instantiate the objects. Display all the values, including the projected graduation dates. Save the application as TestCollegeStudent.java

I've been able to do everything it asks, except when I try to calculate the graduation date, it adds 4 years to the enrollment date value as well. Here is my CollegeStudent class:

import java.util.GregorianCalendar;

public class CollegeStudent {

    String firstName;
    String lastName;
    GregorianCalendar enrollmentDate;
    GregorianCalendar graduationDate;
    
    
    // Constructor requiring first name, last name, and enrollment date
    public CollegeStudent(String first, String last, GregorianCalendar enrollment) {
        firstName = first;
        lastName = last;
        enrollmentDate = enrollment;
        
        // Set graduationDate to enrollmentDate, then add 4 years
        graduationDate = enrollment;
        graduationDate.add(GregorianCalendar.YEAR, 4);
    }
    // Get and Set firstName
    public void setFirstName(String first) {
        firstName = first;
    }
    public String getFirstName() {
        return firstName;
    }
    
    // Get and Set lastName
    public void setLastName(String last) {
        lastName = last;
    }
    public String getLastName() {
        return lastName;
    }
    
    // Get and Set enrollmentDate
    public void setEnrollmentDate(GregorianCalendar enrollment) {
        enrollmentDate = enrollment;
    }
    public GregorianCalendar getEnrollmentDate(){
        return enrollmentDate;
    }
    
    // Get and Set graduationDate
    public void setGraduationDate(GregorianCalendar graduation) {
        graduationDate = graduation;
    }
    public GregorianCalendar getGraduationDate() {
        return graduationDate;
    }

}

My TestCollegeStudent.java displays both the enrollmentDate and graduationDate and they both show the same date 4 years after the initial enrollmentDate entered by the user. If I remove the line where I added 4 years to the graduationDate, they both display as the initial enrollmentDate. I feel like I'm missing some very simple syntax issue, but I haven't been able to figure out what it is.


Solution

  • It is happening because of the following assignment inside the constructor:

    graduationDate = enrollment;
    

    This assignment makes graduationDate reference to the same instance of Calendar which is being referenced by enrollment and therefore a change in the instance brought through any of the references will cause the result to be the same for both the references.

    You can understand it with the following example:

    import java.util.Calendar;
    import java.util.GregorianCalendar;
    
    public class Main {
        public static void main(String[] args) {
            Calendar enrollment = Calendar.getInstance();
            Calendar graduationDate = enrollment;
            System.out.println(enrollment.getTime());
            System.out.println(graduationDate.getTime());
    
            graduationDate.add(GregorianCalendar.YEAR, 4);
            System.out.println(enrollment.getTime());
            System.out.println(graduationDate.getTime());
        }
    }
    

    Output:

    Sat Jan 16 15:31:29 GMT 2021
    Sat Jan 16 15:31:29 GMT 2021
    Thu Jan 16 15:31:29 GMT 2025
    Thu Jan 16 15:31:29 GMT 2025
    

    In order to deal with this situation, graduationDate needs to reference a copy (clone) of the instance being referenced enrollment so that any changes brought through the reference, graduationDate will only be applicable to the clone.

    import java.util.Calendar;
    import java.util.GregorianCalendar;
    
    public class Main {
        public static void main(String[] args) {
            Calendar enrollment = Calendar.getInstance();
            Calendar graduationDate = (Calendar)enrollment.clone();// You have to do this
            System.out.println(enrollment.getTime());
            System.out.println(graduationDate.getTime());
    
            graduationDate.add(GregorianCalendar.YEAR, 4);
            System.out.println(enrollment.getTime());
            System.out.println(graduationDate.getTime());
        }
    }
    

    Output:

    Sat Jan 16 15:30:34 GMT 2021
    Sat Jan 16 15:30:34 GMT 2021
    Sat Jan 16 15:30:34 GMT 2021
    Thu Jan 16 15:30:34 GMT 2025
    

    By the way, the date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.

    Check out Trail: Date Time for step-by-step tutorial and examples of using the java.time API (the modern date-time API).