9

I have created an immutable class with a date field. How do I ensure that even the date field is immutable, since even if you make date field final you can still assign a different value to it later?

Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45
Mona Dhar
  • 149
  • 1
  • 3
  • 6

8 Answers8

17

In your getDate() method, return a new Date() instance, instead of the same instance.

public Date getDate() {
    // Not correct.
    return this.date; // This will make your class mutable.

    // Instead use, 
    return new Date(this.date.getTime()); // This will make sure your date field cannot be changed.
}
Vince
  • 14,470
  • 7
  • 39
  • 84
Codebender
  • 14,221
  • 7
  • 48
  • 85
  • In addition to that, we can do this also in constructor: `public Constructor(Date d){ this.date = new Date(d.getTime());}` so that if changes are made to `d` object are also not affecting date in original object. – Dhruvam Gupta Mar 27 '20 at 07:07
7

java.time

Other Answers are correct that in showing strategies for fixing values within your objects.

Let me also recommend that you use the modern java.time classes rather than the terrible legacy classes. In place of Date, use Instant. In place of Calendar, use ZonedDateTime.

The java.time classes are designed as immutable objects. Methods such as plus…, minus…, to…, and with all produce a fresh object, leaving the original intact. The classes carry no setter methods.

Bonus tip: In your own immutable classes, you may find it useful to follow the method naming patterns established by the java.time classes.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
3

This is an example of a Bean(Class) with an immutable HAS-A Date Object.

 import java.util.Date;


public class MyBean {

    private Date date; // Immutable Date Step 1 Make Private

    public MyBean(Date date)
    {
         // Immutable Date Step 2 If Set through Constructor then get a specialised (sub class) Date.
        this.date= getImmutableDate(date);  // THIS METHOD RETURNS AN IMMUTABLE DATE
    }

    public MyBean(){} // Allow Default Constructor

    public Date getDate() {
        return date;
    }

     // Immutable Date Step 3- Allow setting of date only once!!
    public void setDate(Date date) {
        if(this.date==null)
        {
            this.date= getImmutableDate(date);  
        }
    }

    /* Override all Setter methods of Date class. So even if user gets reference of Date Object it is not the original date object
     * it would be a modified date object whose all setter methods do nothing*/
    private Date getImmutableDate(Date date)
    {
        /* This is an Anonymous Inner Class that extends java.util.Date class, it overrides all the setter methods
         * making the date object IMMUTABLE( i.e setXXX has no effect)
         * */
        date =new Date(date.getTime()){
            private static final long serialVersionUID = 1L;
            @Override
            public void setYear(int year) {}

            @Override
            public void setMonth(int month) {}

            @Override
            public void setDate(int date) {}                

            @Override
            public void setHours(int hours) {}

            @Override
            public void setMinutes(int minutes) {}

            @Override
            public void setSeconds(int seconds) {}



@Override
        public void setTime(long time) {}

    };
    return date;
}

}

Oliver
  • 6,152
  • 2
  • 42
  • 75
  • The Easiest way is using String. But if you want "java.util.Date" then above example will do – Oliver Aug 06 '15 at 05:29
  • Failing silently in the setters like this seems like a bug waiting to happen... @Oliver the time in milliseconds would be better than a String, but the Date is best. – Brian McCutchon Aug 06 '15 at 06:00
  • @McBrainy How do you think this code would fail? Please throw some more light on it. – Oliver Aug 06 '15 at 06:09
  • 2
    @Oliver, someone using your API will get very confused if you have overridden all these methods. This may not be a good programming practice in many cases. – Codebender Aug 06 '15 at 16:20
  • @Codebender Well this works. Using Joda API is a better alternative as I think, as DateTime object is immutable anyways. But if you don't want to use a whole API like Joda this could prove to be a good quick fix. – Oliver Aug 07 '15 at 06:26
3

You can clone date object so that it cannot be modified

public Date getDate (){
         return (Date) this.date.clone ();
}
Saurabh
  • 7,525
  • 4
  • 45
  • 46
2

Here is the simple example of immutable class in Java

public final class Employee{  
    final String pancardNumber;  
    public Employee(String pancardNumber){  
        this.pancardNumber=pancardNumber;  
    }  
    public String getPancardNumber(){  
       return pancardNumber;  
    }
}

The above class is immutable because:

  1. The instance variable of the class is final i.e. we cannot change the value of it after creating an object.
  2. The class is final so we cannot create the subclass.
  3. There is no setter methods i.e. we have no option to change the value of the instance variable.

These points makes this class as immutable. In case of Date attribute, you can use constructor to set your date with every new object and import org.joda.time.DateTime class. This is a better version than the java.util.Date because it is immutable. Using a java.util.Date would be dangerous as it is a mutable class and we can’t control the calling thread (which might modify it). Here is example.

public final class Bill {

    private final int amount;
    private final DateTime dateTime;

    public Bill(int amount, DateTime dateTime) {
        this.amount = amount;
        this.dateTime = dateTime;
    }
    public int getAmount() {
        return amount;
    }
    public DateTime getDateTime() {
        return dateTime;
    }
}  
Avinash Mishra
  • 1,346
  • 3
  • 21
  • 41
  • 1
    Why, exactly, would we be worried about the "calling thread" modifying our private field? Through the getter? You can do as Codebender or Joni and effectively clone the Date when returning it from a getter. – Brian McCutchon Aug 06 '15 at 05:45
  • @McBrainy: Good point. I agree with you. We can do it in that way. – Avinash Mishra Aug 06 '15 at 06:01
1

Identify the mutable instance variables(like date or hashmap) return new objects with copied content for all mutable objects. Immutable variables can be returned safely without extra effort.

See below example:

import java.util.Date;

public final class ImmutableClass
{

    /**
    * Integer class is immutable as it does not provide any setter to change its content
    * */
    private final Integer immutableField1;
    /**
    * String class is immutable as it also does not provide setter to change its content
    * */
    private final String immutableField2;
    /**
    * Date class is mutable as it provide setters to change various date/time parts
    * */
    private final Date mutableField;

    //Default private constructor will ensure no unplanned construction of class
    private ImmutableClass(Integer fld1, String fld2, Date date)
    {
        this.immutableField1 = fld1;
        this.immutableField2 = fld2;
        this.mutableField = new Date(date.getTime());
    }

    //Factory method to store object creation logic in single place
    public static ImmutableClass createNewInstance(Integer fld1, String fld2, Date date)
    {
        return new ImmutableClass(fld1, fld2, date);
    }

    //Provide no setter methods

    /**
    * Integer class is immutable so we can return the instance variable as it is
    * */
    public Integer getImmutableField1() {
        return immutableField1;
    }

    /**
    * String class is also immutable so we can return the instance variable as it is
    * */
    public String getImmutableField2() {
        return immutableField2;
    }

    /**
    * Date class is mutable so we need a little care here.
    * We should not return the reference of original instance variable.
    * Instead a new Date object, with content copied to it, should be returned.
    * */
    public Date getMutableField() {
        return new Date(mutableField.getTime());
    }

    @Override
    public String toString() {
        return immutableField1 +" - "+ immutableField2 +" - "+ mutableField;
    }
}

For more details : http://www.javainterviewpoint.com/make-class-immutable-java/

Naveen Yadav
  • 203
  • 9
  • 25
0

Use defensive copying. This means that if you have to pass the date to some other class, for example through a method parameter or returned from a method, you should make a copy.

Making a copy of a Date is easy:

new Date(myDate.getting())
Joni
  • 108,737
  • 14
  • 143
  • 193
0

Only way to do this. Create a custom DateClass and Overrider the set** methods and dont provide any functionality to this methods.

Prasad
  • 1,089
  • 13
  • 21