How can I make the following class immutable?

Viewed 183

I know I need to make Date immutable. However, I am not sure what else needs to be amended to ensure the class is immutable. final has been declared for the class and its methods.

public final class Journal {
    private final Set<Article> edition;
    private final Date pubDate;

    public Journal(Article [] contents, Date d) {
        edition = new TreeSet<Article>();
        for(int i=0; i <contents.length; i++)
        edition.add(contents[i]);

        pubDate = d;
    }

    public Set<Article> getArticles() {
        return Collections.unmodifiableSortedSet(edition);
    }

    public Date getDate() { return pubDate; }
}   
3 Answers

There are several kinds of immutability. If you want standard immutability, that means every instance variable is final and there are no setters. If you want deep immutability, then your class must be immutable, but so must all of its instance variables. Alternatively, you can have your getters only return copies of your instance variables (ensuring that they will never change within your class). However, this carries a not insignificant performance cost and can create technical debt later on.

I believe Date class you are using in java.util.Date. So, Date is mutable in java (not sure what is the reason). So, you should return a copy of pubDate.

public Date getDate() 
{
 return pubDate.clone();
}

Also, A new class has added in LocalDate which is immutable. I would suggest to use that if you your program does not require java.util.Date specifically.

EDIT: As per the suggestion in comment Date#clone can lead to problem as Date can be sub classed. Use new Date(pubDate.getTime()) for copying the Date object.

The best advice I could give you is to use Instant (or LocalDateTime or ZonedDateTime) in place of a Date because Instant (and the other java.time classes) are immutable. Date is obsolete and should no longer be used. But, if you need to stick to this code then you could do defensive copies, something like the following:

public Journal(Article [] contents, Date d) {
    // ...
    pubDate = new Date(d.getTime());
}

// ...

public Date getDate() {
    return new Date(pubDate.getTime());
}

Notice here that the Date.clone method was not used. This is because Date is non-final, so the clone method is not guaranteed to return an object whose class is java.util.Date, it could return an instance of an untrusted subclass that is specifically designed with malicious intentions.

Related