Why is this class mutable?

Viewed 3219
public class Test {
    private final String url;
    public Test(String url) {
        this.url = url;
    }
    public String getUrl() {
        return url;
    }
}

The Test class has:

  1. Only one instance variable which is private and final.
  2. No setters.
  3. The only way to initialize the instance variable is through the constructor.
  4. And once the URL is set, it can't be modified even in getUrl even if that method is overridden by any subclass of Test.

But a book that I am reading says the above Test class is mutable because:

  • Neither class is final so that it can be extended, and a subclass can override instance methods. But the Test class does not really have any instance methods other than the constructor.

  • Nor is the constructor private.

Can you please help me in understanding why the Test class is mutable?

2 Answers

An arbitrary instance of Test isn't guaranteed to be immutable, although direct instances of Test are. But consider this subclass:

public class MutableTest extends Test {
        private int mutable;
        public MutableTest(String url) {
                super(url);
        }

        @Override
        public String getUrl() {
                return super.getUrl() + mutable++;
        }
}

Then you can write something like this:

Test instance = new MutableTest("http://example.com/");
String firstGet = instance.getUrl();
String secondGet = instance.getUrl();
assertEquals(firstGet, secondGet); // Boom!

An object that receives an object which is known to be of type Test would know the object to be immutable. An object receiving a non-null reference of type Test, however, would have no language-defined way of ensuring that the object identified thereby isn't mutable. Some people seem to regard this as a major worry. In general, however, I don't think it should be.

If a class is usefully inheritable, it will generally be easy to contrive a derived type that would be totally unsuitable for any non-contrived purpose. The only ways by which a language could even try to prevent that would be by greatly limiting the range of useful things that derived types can do. Outside of a few specialized kinds of classes (typically those related to security), however, it's generally better to ensure that derived classes can do useful things than worry about the possibility of them doing contrived and useless things.

Related