immutable objects and lazy initialization.

后端 未结 4 1807
悲哀的现实
悲哀的现实 2021-01-20 01:31

http://www.javapractices.com/topic/TopicAction.do?Id=29

Above is the article which i am looking at. Immutable objects greatly simplify your program, since they:

相关标签:
4条回答
  • 2021-01-20 01:40

    And to add to other answers.

    Immutable object cannot be changed. The final keyword works for basic data types such as int. But for custom objects it doesn't mean that - it has to be done internally in your implementation:

    The following code would result in a compilation error, because you are trying to change a final reference/pointer to an object.

    final MyClass m = new MyClass();
    m = new MyClass();
    

    However this code would work.

    final MyClass m = new MyClass();
    m.changeX();
    
    0 讨论(0)
  • 2021-01-20 01:43

    If your object is immutable it can't change it's state and therefore it's hashcode can't change. That allows you to calculate the value once you need it and to cache the value since it will always stay the same. It's in fact a very bad idea to implement your own hasCode function based on mutable state since e.g. HashMap assumes that the hash can't change and it will break if it does change.

    The benefit of lazy initialization is that hashcode calculation is delayed until it is required. Many object don't need it at all so you save some calculations. Especially expensive hash calculations like on long Strings benefit from that.

    class FinalObject {
        private final int a, b;
        public FinalObject(int value1, int value2) {
            a = value1;
            b = value2;
        }
    
        // not calculated at the beginning - lazy once required
        private int hashCode;
        @Override
        public int hashCode() {
            int h = hashCode; // read
            if (h == 0) {
                h = a + b;    // calculation
                hashCode = h; // write
            }
            return h;         // return local variable instead of second read
        }
    }
    

    Edit: as pointed out by @assylias, using unsynchronized / non volatile code is only guaranteed to work if there is only 1 read of hashCode because every consecutive read of that field could return 0 even though the first read could already see a different value. Above version fixes the problem.

    Edit2: replaced with more obvious version, slightly less code but roughly equivalent in bytecode

    public int hashCode() {
        int h = hashCode; // only read
        return h != 0 ? h : (hashCode = a + b);
        //                   ^- just a (racy) write to hashCode, no read
    }
    
    0 讨论(0)
  • 2021-01-20 01:59

    What that line means is, since the object is immutable, then the hashCode has to only be computed once. Further, it doesn't have to be computed when the object is constructed - it only has to be computed when the function is first called. If the object's hashCode is never used then it is never computed. So the hashCode function can look something like this:

    @Override public int hashCode(){
        synchronized (this) {
            if (!this.computedHashCode) {
                this.hashCode = expensiveComputation();
                this.computedHashCode = true;
            }
        }
        return this.hashCode;
    }
    
    0 讨论(0)
  • 2021-01-20 02:00

    As explained by others, because the state of the object won't change the hashcode can be calculated only once.

    The easy solution is to precalculate it in the constructor and place the result in a final variable (which guarantees thread safety).

    If you want to have a lazy calculation (hashcode only calculated if needed) it is a little more tricky if you want to keep the thread safety characteristics of your immutable objects.

    The simplest way is to declare a private volatile int hash; and run the calculation if it is 0. You will get laziness except for objects whose hashcode really is 0 (1 in 4 billion if your hash method is well distributed).

    Alternatively you could couple it with a volatile boolean but need to be careful about the order in which you update the two variables.

    Finally for extra performance, you can use the methodology used by the String class which uses an extra local variable for the calculation, allowing to get rid of the volatile keyword while guaranteeing correctness. This last method is error prone if you don't fully understand why it is done the way it is done...

    0 讨论(0)
提交回复
热议问题