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

Re: the benefits of immutability



Seth Gordon wrote:

 > Object.equals() has a number of stated invariants, too (e.g.,
 > transitivity), but they're documented, anyone who overloads the
 > method is responsible for following those invariants.  Why can't the
 > immutability of String objects be handled the same way?

Dan Sugalski and James Y. Knight have already answered the part of the
question that deals with the finality of String.  A much less
informative answer can be found in the Java FAQ under item 13.6:

http://java.sun.com/people/linden/faq_b.html

It's funny you should bring "equals" up though, because the documented
contract of the equals method is actually next to impossible to
fulfill, if you override this method in more than one subclass in
the inheritance chain.  The contract is basically this:

 (a) the "equals" method must define an equivalence relationship, i.e.
     one that is reflexive, symmetric, and transitive.

 (b) if you override "equals", you must also override "hashCode" in a
     consistent manner, so as to not break HashMap-dependent classes.


Now suppose you have a class Superclass that overrides the "equals"
method.  Suppose Subclass extends Superclass and adds a new aspect of
state, some new instance field that the superclass does not have.  It
is rather hard to write a useful "equals" method in a way that takes
the new extra state into account and at the same time meets the
documented invariants of the "equals" method.  You can try for
yourself:

    public class Superclass {

        private String m_str;

        public Superclass(String str) {
            m_str = str;
        }

        public boolean equals(Object obj) {
            if ( !(obj instanceof Superclass) ) { return false; }

            return ((String) obj).equals(m_str);
        }

        public int hashCode() {
            return m_str == null ? 0 : m_str.hashCode();
        }
    }

    class Subclass extends Superclass {
        private int m_num;

        public Subclass(String str, int num) {
            super(str);
            m_num = num;
        }

        public boolean equals(Object obj) {
            // try and replace this with an implementation that takes
            //  m_num into account
            return super.equals(obj);
        }
    }


But this is neither here, nor there. Overall, you're right, of course.
It would be possible to make String non-final and simply deal with all
the headaches this would cause.  For example, Python allows you to
subclass immutable types.  The 'str' (string) type is immutable, yet
it can be subclassed:

    class MyString(str):
        pass

Observe, however, the complications that had to be introduced in order
to allow immutable classes to be subclassed, while preserving their
immutability:

http://www.python.org/2.2.2/descrintro.html#__new__

If you worked with Python prior to release 2.2, I am sure you will
agree that the new initialization sequence is noticeably harder to
understand than the old one.