Tuesday, August 14, 2012

immutable classes in java

I've learned about designing immutable objects since Joshua Bloch published "Effective Java" book, but I never really practice it. I was very used to a traditional java beans with setters and getters, especially that my IDE can generate it for me.  Another reason is that we generate pojo from .xsd using jaxb, and jaxb does not generate immutable class yet.

But since I started using scala, I realized the value of having immutable objects in your code base.
Immutable objects greatly simplify your program, since they :
  • are simple to construct, test, and use
  • are automatically thread-safe and have no synchronization issues
  • do not need a copy constructor
  • do not need an implementation of clone
  • allow hashCode to use lazy initialization, and to cache its return value
  • do not need to be copied defensively when used as a field
  • make good Map keys and Set elements (these objects must not change state while in the collection)
  • have their class invariant established once upon construction, and it never needs to be checked again
  • always have "failure atomicity" (a term used by Joshua Bloch) : if an immutable object throws an exception, it's never left in an undesirable or indeterminate state
Here's an example of an immutable object.

public final class User {
    private final String name;
    private final String username;
    private final String password;
    private final int permission;
    
    private int hashCode;

    public User(String name, String username, String password, int permission) {
        this.name = name;
        this.username = username;
        this.password = password;
        this.permission = permission;
    }

    public String getName() {
        return name;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public int getPermission() {
        return permission;
    }

    @Override
    public int hashCode() {
         //This is Lazily loading. 
         if (hashCode == 0) {
            int result = name.hashCode();
            result = 31 * result + username.hashCode();
            result = 31 * result + password.hashCode();
            result = 31 * result + permission;
            hashCode = result;
        }
        return hashCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (permission != user.permission) return false;
        if (!name.equals(user.name)) return false;
        if (!password.equals(user.password)) return false;
        if (!username.equals(user.username)) return false;

        return true;
    }
}


But what if you have more than 4 fields? It is not going to be pretty having a constructor with more than 4 parameters.

In that case, you can add a Builder.


public final class User {
    private final String name;
    private final String username;
    private final String password;
    private final int permission;
    private final Date createDate;
    private final Date updateDate;

    private int hashCode;

    private User(Builder builder) {
        this.name = builder.name;
        this.username = builder.username;
        this.password = builder.password;
        this.permission = builder.permission;
        this.createDate = builder.createDate;
        this.updateDate = builder.updateDate;
    }

    public String getName() {  return name; }

    public String getUsername() { return username; }

    public String getPassword() { return password; }

    public int getPermission() { return permission; }

    public Date getCreateDate() { return createDate; }

    public Date getUpdateDate() { return updateDate; }

     //equals and hashCode are ommitted.


    //User Builder
    public static class Builder {
        private String name;
        private String username;
        private String password;
        private int permission;
        private Date createDate;
        private Date updateDate;

        public Builder(String username, String password) {
            this.username = username;
            this.password = password;
        }

        public Builder(User user) {
            this.name = user.getName();
            this.username = user.getUsername();
            this.password   = user.getPassword();
            this.permission = user.getPermission();
            this.createDate = user.getCreateDate();
            this.updateDate = user.getUpdateDate();
        }

        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        
        public Builder setUsername(String username) {
            this.username = username;
            return this;
        }

        public Builder setPassword(String password) {
            this.password = password;
            return this;
        }
        
        public Builder setPermission(int permission) {
            this.permission = permission;
            return this;
        }        

        public Builder setCreateDate(Date createDate) {
            this.createDate = createDate;
            return this;
        }
        public Builder setUpdateDate(Date updateDate) {
            this.updateDate = updateDate;
            return this;
        }

        public User build() { return new User(this); }

    }

}
 
This is how you use it:
 
 
User user = new User.Builder("username","pasword").setCreateDate(new Date()).setName("Allan").setPermission(1).build();
        
//to change the name
user  = new User.Builder(user).setName("Homer").build();
        
//change password
user = new User.Builder(user).setPassword("secret").build(); 


Now what happened, if you wanna use immutable Pojo in Hibernate?  You can do so, by

  1. Add @Access annotation in the class level
  2. Make hashCode transient

Example:

@Entity
@Table(name = "system_monitor")
@SequenceGenerator(name="PK",sequenceName="system_monitor_id_SEQ")
@Access(AccessType.FIELD)
public class SystemMonitor {
    
    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PK")
    private Long id;  
 
    ...
 
 
 
     

This looks cool, but it requires a lot of coding. I agree with you. This is why I created this ImmutablePojoGenerator, to help you generate the code. You download the app here - immutablePojoGenerator