0
class A {

    Object obj;

}

Say I have this class with the field obj. I want to make this field's value fixed once assigned.

The problem is that I don't want to assign the value in the constructor thus I cannot make the field final. Then it came up with me another solution:

class A {

    Object obj;

    public void setObj(Object obj) {
        if (obj ==null)
            this.obj = obj;
    }
}

I wonder if there is a better (more elegant) one.

bijiDango
  • 1,385
  • 3
  • 14
  • 28
  • 4
    Your implementation is not thread safe (though without reads or indication if the value has changed - it might not be an issue, but odds are, it will become one if you try to make your project scale better later on) – amit Feb 25 '15 at 11:50
  • And what is supposed to happen if one attempts to set the field twice? – fge Feb 25 '15 at 11:51
  • 1
    and please, refrain from calling classes `a`, it hurts the eyes of any java developer, since classes should start with upper case letter (strong java convention) – amit Feb 25 '15 at 11:52
  • @fge Not sure yet. Maybe a exception or maybe not. The focus is the value not be changed. – bijiDango Feb 25 '15 at 11:54
  • 2
    See http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicReference.html#compareAndSet%28V,%20V%29. I would throw an exception is set a second time, or at least return a boolean as compareAndSet does. – JB Nizet Feb 25 '15 at 11:54
  • Change your method to `public synchronized void setObj(Object obj)` – Héctor Feb 25 '15 at 11:57
  • 2
    I would re-consider how much you "don't want to assign the value in the constructor" and find a way to do this instead. – Peter Lawrey Feb 25 '15 at 11:58
  • Problem is here: "I don't want to assign the value in the constructor". Really ... believe me ... you want! – Seelenvirtuose Feb 25 '15 at 12:01
  • @PeterLawrey I'll think twice. But isn't this problem actually occuring somewhere. So I am still curious about the answer. – bijiDango Feb 25 '15 at 12:14

1 Answers1

0

An alternative is to use functional programming idiom, and make sure your objects are immutable, by returning a new object.

The idea is the "new object" does not let you modify the field, and the "old object" is creating a new instance (and returning it) instead of simply setting the field, when asked to. This guaranteed your field is still final, and you can only change it once.

class B {
    private static class ValuedB extends B {
        public ValuedB(Object obj) {
            super(obj);
        }
        @Override
        public Object getObj() {
            return super.obj;
        }
        @Override
        public B setObj(Object obj) {
            return this;
        }
    }
    public B() { 
        this(null);
    }
    private B(Object obj) {
        this.obj = obj;
    }
    private final Object obj;
    public Object getObj() { return null;/* or raise exception */ }
    public B setObj(Object obj) { 
        return new ValuedB(obj);
    }

}

Invoking with myB = b.setObj(...);

Draw back is of course if you already possess multiple references to an instance of B, only one will me modified, so if it is the case - this idiom might not fit.


For example, running:

public static void main(String[] args) {
    B b = new B();
    System.out.println(b.getObj());
    b = b.setObj("asdf");
    System.out.println(b.getObj());
    b = b.setObj("qwerty");
    System.out.println(b.getObj());
}

will yield, as expected:

null
asdf
asdf
amit
  • 175,853
  • 27
  • 231
  • 333
  • Interesting solution. It takes me minutes to understand it a little. So this class can only hold one field? – bijiDango Feb 25 '15 at 12:21
  • @bijiDango You can modify it, but if you want multiple "set only once" fields - it might become unscalable (though you might be able to wrap it to overcome it, didn't think about it) – amit Feb 25 '15 at 12:23