开发者

Possibilities of creating immutable class in Java

what are possibilities of cre开发者_如何学Pythonating immutable bean in Java. For example I have immutable class Person. What's a good way to create instance and fill private fields. Public constructor doesn't seems good to me because of a lot input parameters will occure as class will grow in rest of application. Thank you for any suggestions.

public class Person {

private String firstName;
private String lastName;
private List<Address> addresses;
private List<Phone> phones;

public List<Address> getAddresses() {
    return Collections.unmodifiableList(addresses);
}

public String getFirstName() {
    return firstName;
}

public String getLastName() {
    return lastName;
}

public List<Phone> getPhones() {
    return Collections.unmodifiableList(phones);
}
}

EDIT: Specify question more precisely.


You could use the builder pattern.

public class PersonBuilder {
  private String firstName;
  // and others...

  public PersonBuilder() {
    // no arguments necessary for the builder
  }

  public PersonBuilder firstName(String firstName) {
    this.firstName = firstName;
    return this;
  }

  public Person build() {
    // here (or in the Person constructor) you could validate the data
    return new Person(firstName, ...);
  }
}

You can then use it like this:

Person p = new PersonBuilder.firstName("Foo").build();

At first sight it might look more complex than a simple constructor with tons of parameters (and it probably is), but there are a few significant advantages:

  • You don't need to specify values that you want to keep at the default values
  • You can extend the Person class and the builder without having to declare multiple constructors or needing to rewrite every code that creates a Person: simply add methods to the builder, if someone doesn't call them, it doesn't matter.
  • You could pass around the builder object to allow different pieces of code to set different parameters of the Person.
  • You can use the builder to create multiple similar Person objects, which can be useful for unit tests, for example:

    PersonBuilder builder = new PersonBuilder().firstName("Foo").addAddress(new Address(...));
    Person fooBar = builder.lastName("Bar").build();
    Person fooBaz = builder.lastName("Baz").build();
    assertFalse(fooBar.equals(fooBaz));
    


You should have a look at the builder pattern.


One good solution is to make your fields final, add your constructor private and make use of Builders in your code. In our project we combined the Builder pattern with a validation framework so that once an object is created we are sure it's immutable and valid.

Here is a quick example:

public class Person {

public static class Builder {

    private String firstName;
    private String lastName;
    private final List<String> addresses = new ArrayList<String>();
    private final List<String> phones = new ArrayList<String>();

    public Person create() {
        return new Person(firstName, lastName, addresses, phones);
    }

    public Builder setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public Builder setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

    public Builder addAddresse(String adr) {
        if (adr != null) {
            addresses.add(adr);
        }
        return this;
    }

    public Builder addPhone(String phone) {
        if (phone != null) {
            phones.add(phone);
        }
        return this;
    }
}

// ************************ end of static declarations **********************

private final String firstName;
private final String lastName;
private final List<String> addresses;
private final List<String> phones;

private Person(String firstName, String lastName, List<String> addresses, List<String> phones) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.addresses = addresses;
    this.phones = phones;
}

public List<String> getAddresses() {
    return Collections.unmodifiableList(addresses);
}

public String getFirstName() {
    return firstName;
}

public String getLastName() {
    return lastName;
}

public List<String> getPhones() {
    return Collections.unmodifiableList(phones);
}
}

In my example you can see that all the setters in the Builder return the Builder instance so that you can easily chain the setters calls. That's pretty useful.

You could take a look at the Builder pattern presented by Joshua Bloch.

As I said before, combined with a validation framework (see for ex. http://www.hibernate.org/subprojects/validator.html) this is really powerfull.


With interfaces. Do this:

public interface Person {
    String getFirstName();
    String getLastName();

    // [...]
}

And your implementation:

// PersonImpl is package private, in the same package as the Factory
class PersonImpl {

    String getFirstName();
    void setFirstName(String s);
    String getLastName();
    void setLastName(String s);

    // [...]
}

// The factory is the only authority to create PersonImpl
public class Factory {

    public static Person createPerson() {
        PersonImpl result = new PersonImpl();

        // [ do initialisation here ]

        return result;
    }
}

And never expose the implementation to the places where you want Person to be immutable.


Initializing in the constructor is nevertheless the simplest and safest way to achieve immutability, as this is the only way to have final fields in your immutable class (which is the standard idiom, and has beneficial effects especially if your class is used in a multithreaded environment). If you have lots of properties in your class, it may be a sign that it is trying to do too much. Consider dividing it to smaller classes, or extracting groups of related properties into compound property classes.

Using a Builder (with a private constructor) is a possibility, however it still needs a way to set the properties of the object being built. So you fall back to the original dilemma of constructor parameters vs accessing the private members. In the latter case you can't declare the properties of the object being built as final, which IMHO is a great minus. And in the former case you still have the same long list of constructor parameters you wanted to avoid in the first place. Just now with a lot of extra boilerplate code on top of it.


You can achieve an "immutable" bean by making a read-only interface and then making the implementation into a mutable bean. Passing around the interface won't allow for mutation, but when you construct the object and have the implementation, you can do all sorts of bean-y things:

public interface Person {
   String getFirstName();
   String getLastName();
   // ... other immutable methods ...
}

public class MutablePerson implements Person {
   // ... mutable functions, state goes here ...
}


Use the factory-pattern:

  • let Person be an interface with only "get"-functions
  • create a PersonFactory with an appropriate API for building a Person-object
  • the PersonFactory creates an object which implements the Person-interface and returns this


  1. Have final fields.
  2. Make the class as "final" class by declaring as final public class Person
  3. do not use setXXX() methods to set the value since it will change the state of a variable. however getXXX() methods are allowed.
  4. Use a private constructor so that you can set fields using the constructor itself.

Follow the above guidelines for Immutable class.


Use final fields for all your instance variables. You can create a constructor if you like and choose to not expose setters, e.g.,

public class Person {
   private final String firstName;
   ....

   public Person(String firstName, ... ) {
       this.firstName = firstName;
   }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜