Do effectively immutable objects make sense?

后端 未结 4 1978
广开言路
广开言路 2021-02-20 16:24

In the book Java Concurrency In Practice it explains the advantages of \"effectively immutable\" objects versus mutable objects concurrency-wise. But it does not explain what a

相关标签:
4条回答
  • 2021-02-20 16:57

    For circular immutables:

    class Foo
    {
        final Object param;
        final Foo other;
    
        Foo(Object param, Foo other)
        {
            this.param = param;
            this.other = other;
        }
    
        // create a pair of Foo's, A=this, B=other
        Foo(Object paramA, Object paramB)
        {
            this.param = paramA;
            this.other = new Foo(paramB, this);
        }
    
        Foo getOther(){ return other; }
    }
    
    
    
    // usage
    Foo fooA = new Foo(paramA, paramB);
    Foo fooB = fooA.getOther();
    // publish fooA/fooB (unsafely)
    

    A question is, since this of fooA is leaked inside constructor, is fooA still a thread safe immutable? That is, if another thread reads fooB.getOther().param, is it guaranteed to see paramA? The answer is yes, since this is not leaked to another thread before the freeze action; we can establish hb/dc/mc orders required by spec to prove that paramA is the only visible value for the read.

    Back to your original question. In practice there are always constraints beyond the pure technical ones. Initialize everything inside constructor is not necessarily the best option for a design, considering all engineering, operational, political and other human-ish reasons.

    Ever wondering why we are fed to think that it is a great supreme idea?

    The deeper problem is Java lacks a general cheap fense for safe publication which is cheaper than volatile. Java only has it for final fields; for some reason, that fence is not available otherwise.

    Now final carries two independent meanings: 1st, that a final field must be assigned exactly once; 2nd, the memory semantics of safe publication. These two meanings have nothing to do with each other. It is quite confusing to bundle them together. When people need the 2nd meaning, they are forced to accept the 1st meaning too. When the 1st is very inconvenient to achieve in a design, people wonder what they have done wrong - not realizing that it's Java that did wrong.

    Bundling of two meanings under one final makes it double plus good, so that apparently we have more reason and motivation to use final. The more sinister story is actually we are forced to use it because we are not given a more flexible choice.

    0 讨论(0)
  • 2021-02-20 17:00

    Yes, they make sense in some cases. An easy example is when you want some property to be generated lazily and cached so you can avoid the overhead of generating it if it's never accessed. String is an example of an effectively immutable class that does this (with its hashcode).

    0 讨论(0)
  • 2021-02-20 17:08

    Suppose one has an immutable class Foo with five properties, named Alpha, Beta, etc., and one wishes to provide WithAlpha, WithBeta, etc. methods which will return an instance which is identical to the original except with the particular property changed. If the class is truly and deeply immutable, the methods have to take the form:

    Foo WithAlpha(string newAlpha)
    { 
      return new Foo(newAlpha, Beta, Gamma, Delta, Epsilon);
    }
    
    Foo WithBeta(string newBeta) 
    {
      return new Foo(Alpha, NewBeta, Gamma, Delta, Epsilon);
    }
    

    Ick. A massive violation of "Don't Repeat Yourself" (DRY) principles. Further, adding a new property to the class would require adding it to every single one of those methods.

    On the other hand, if each Foo held an internal FooGuts which included a copy constructor, one could instead do something like:

    Foo WithAlpha(string newAlpha)
    {
      FooGuts newGuts = new FooGuts(Guts); // Guts is a private or protected field
      newGuts.Alpha = newAlpha;
      return new Foo(newGuts); // Private or protected constructor
    }
    

    The number of lines of code for each method has increased, but the methods no longer need to make any reference to any properties they aren't "interested" in. Note that while a Foo might not be immutable if its constructor were called with a FooGuts to which any outside reference existed, its constructor is only accessible to code which is trusted not to maintain any such reference after construction.

    0 讨论(0)
  • 2021-02-20 17:12

    Using effectively immutable objects lets you avoid creating a considerable number of classes. Instead of making pairs of [mutable builder]/[immutable object] classes, you can build one effectively immutable class. I usually define an immutable interface, and a mutable class that implements this interface. An object is configured through its mutable class methods, and then published through its immutable interface. As long as the clients of your library program to the interface, to them your objects remain immutable through their published lifetime.

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