[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: the benefits of immutability



Michael St . Hippolyte wrote:

> > Have you read the example I cited earlier:

 > > http://courses.dce.harvard.edu/~cscie160/EffectiveJava.htm
 >
 > Thanks for the reference, I missed it the first time.  As I see it,
 > in the example it uses to make its argument (InstrumentedHashSet)
 > the real culprit is not inheritance but mutability...  I would be
 > curious to see if anyone can come up with a similar example showing
 > broken behavior when functional restrictions are in place,
 > i.e. objects are immutable and methods are side-effect free.

Sure thing.  Here's an example that exhibits the same problem, even
though all the defined classes are immutable and their methods are
side-effect free

    public interface IPinhead {

        /**
         * Returns a pinhead that contains all of the angels contained
         * in this pinhead plus the additional angel passed in as a
         * parameter.
         **/
        IPinhead add(Angel angel);

        /**
         * Works like {@link #add(Angel)}.  The returned value
         * contains all of the angels in this pinhead plus the
         * additional angels passed in the <code>angels</code> array,
         * if any.
         **/
        IPinhead addAll(Angel[] angels);
    }

    public class Pinhead implements IPinhead {
        private final Set m_angels;

        public Pinhead() {
            m_angels =  new HashSet();
        }

        protected Pinhead(Collection angels, Angel angel) {
            m_angels = new HashSet(angels);
            m_angels.add(angel);
        }

        private IPinhead _add(Angel angel) {
            return new Pinhead(m_angels, angel);
        }

        public IPinhead add(Angel angel) {
            return _add(angel);
        }

        public IPinhead addAll(Angel[] angels) {
            IPinhead result = this;
            for (int ii=0; ii<angels.length; ii++) {
                result = add(angels[ii]);
            }
            return result;
        }
    }

    public final class InstrumentedPinhead extends Pinhead {
        private int m_count;

        public InstrumentedPinhead() {
            m_count = 0;
        }

        public IPinhead add(Angel angel) {
            m_count++;
            return super.add(angel);
        }

        public IPinhead addAll(Angel[] angels) {
            m_count += angels.length;
            return super.addAll(angels);
        }

        public int getCount() {
            return m_count;
        }
    }


I guess you could argue that mutability has not been completely
eliminated in this example.

An alternative way to look at it is to notice that the real culprit is
self-use.  The implementation of the "addAll" method in the Pinhead
class makes use of the (public) method "add" defined in the same
class.  In this particular case, self-use can be easily eliminated.
Replace "add" with "_add" in the implementation of "addAll".  In cases
where self-use cannot be eliminated, it must be documented.  (In other
words, you must disclose certain implementation details for some of
the methods in your externally exported API, thus potentially breaking
encapsulation.)