2

I have an object:

Object obj = create();

... and I need to set some properties of this Object:

obj.setParam("7696969", 0x506);

After this procedure I need to make sure that obj cannot be modified.

Is there a way to set obj as final without creating another final Object that copies obj?

Mena
  • 47,782
  • 11
  • 87
  • 106
AndreaF
  • 11,975
  • 27
  • 102
  • 168

5 Answers5

11

Consider using the Builder design pattern for your newly created object.

FooBuilder builder = new FooBuilder();
builder.setParam(...);
builder.setBar(...);

final Foo myFoo = builder.build();

Note that even though myFoo is final, it's member fields may not be. You should not forget to declare those final as well.

Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • 1
    Note the type Foo here would need to be immutable if it shouldn't be modified. – William Morrison Aug 22 '13 at 20:43
  • 4
    +1, by the way, I think it is more elegant to use chain of calls for builders, e.g.: Foo myFoo = Foo.builder().withParam(...).withBar(...).build(); – udalmik Aug 22 '13 at 20:47
  • You might want to take a look at [Lombok's Builder annotation](http://projectlombok.org/features/experimental/Builder.html). It's still marked as "experimental", but it's very close to make it to core. – Idan Arye Aug 22 '13 at 21:14
3

The final keyword doesn't keep the internal variables of an object from being modified. All the final keyword does is keep a variable from being reassigned.

Keeping internals from being modified would require specific design of the object itself.

Objects whose inner variables cannot be reassigned are referred to as immutable objects. You can create an immutable object by using private access modifiers, and creating no set functions. Also be sure to declare all internal variables as final.

Amir's reference to the Builder design is a great suggestion. An immutable object with the builder pattern will work great for you.

William Morrison
  • 10,953
  • 2
  • 31
  • 48
0

If you really need to set parameters separately through setters, then you'll have to do some simple algorithm. For instance, you can provide a boolean marking when the object must become immutable, set that boolean to true after your initialization procedure, all your setters will check for this boolean before deciding if they can reset current value with the new value.

When you don't have native mechanism doing the hard job for you, you have to think up an algorithm that'll do the job :)

OOEngineer
  • 447
  • 3
  • 12
0

You want an immutable class. Here's an example of one.

class Foo
{
    private string s;
    private int i;

    public Foo(string s, int i)
    {
        this.s = s;
        this.i = i;
    }

    public string getS() { return s; }
    public int getI() { return i; }
}

The Builder design pattern is often overkill.

pamphlet
  • 2,054
  • 1
  • 17
  • 27
  • This only works if the constructor is the only way of setting a value. What if you don't know all values when the constructor is called, but rather they're added along the way? – Jeroen Vannevel Aug 22 '13 at 20:56
  • For example, when you want to have a bidirectional reference. – Sotirios Delimanolis Aug 22 '13 at 20:58
  • Then something more sophisticated is required. It would depend on the scenario. I would just hate to leave OP with the impression that the Builder design pattern was required if all that is needed is an immutable. – pamphlet Aug 22 '13 at 21:01
0

Without using the Builder pattern what you can do is

    public class MyImmutableClass {
         final private String foo;
         final private String bar;
         public MyImmutableClass(String f, String b) {
              this.foo = f;
              this.bar = b;
         }

         // only getters
    }

The way it work is

  1. make all fields final
  2. provide no setters
  3. make sure subclass can't override any method ( make the class final )

The builder pattern comes handy in case the number of params in the constructor increases. You are just using the builder to make your code more maintainable and readable but the strategy is almost same i.e it also permit object manipulation only through the inner static class and expose read only (getters) methods in the main/outer class

Another reason for using builder could be in a situation where your object has some mandatory as well as optional parameters.

    public class Item {
       // private fields & getters

         public static class Builder {
              // same private fields
              public Builder(String f, String b) {
                 // set values
              }
              ...
              ...
         }
     }

Here you can use the inner public Builder(String f, String b) constructor to take two mandatory parameters keeping the rest as optional

If the number of parameters is less I think the first strategy work much better than implementing builder, as it has the drawbacks of code duplication (builder needs to copy all fields from the outer class)

Hild
  • 2,625
  • 3
  • 22
  • 22