2

Can a class that does not have final modifier in it be fully immutable ?

For example, is the following class immutable ?

class Animal
{
    private String animalName;

    public Animal(String name) {
        animalName = name;
    }

    public String getName() { return animalName; }
}
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
user2434
  • 6,339
  • 18
  • 63
  • 87
  • When you ask if something is immutable, you have to make some reasonable assumptions about how the object is used. e.g. if you use reflections, nothing is immutable. – Peter Lawrey Nov 02 '11 at 08:16

5 Answers5

8

Yes.

The class (as depicted in your question) is immutable, as none of its internal state may be changed. Even if you were to define a subclass of the Animal class, it could not change animalName; however, while the Animal class is immutable, its subclasses may or not be immutable (depending on their implementation).

The danger of doing this is that if someone were to define a subclass as an inner class within the Animal class (as follows), then they could violate your immutability:

class Animal {
    private String animalName;
    public Animal(String name) {
        animalName = name;
    }
    public getName() { return animalName; }

    public class Eagle extends Animal {
        public Eagle() {
            super("Eagle");
        }
        public void foo() {
            animalName = animalName + "!";
        }
    }
}

For this reason, it's great to use private visibility and the final modifier, wherever possible. This will prevent people from accidentally introducing code that violates immutability or encapsulation constraints that you have meant to impose. That way, it must be a conscious decision on the programmer's part to increase the visibility or remove the final keyword, and therefore they shall not introduce any "accidents" as described above.

Jon Newmuis
  • 25,722
  • 2
  • 45
  • 57
  • I'm not clear what you mean by 'they could violate your immmutability'? It looks like you've just rewritten the class to include an inner subclass? Are there any issues that could occur if your subclass was separate rather than an inner class? Thanks - I'm sure I'm missing something! :-) – Bruce Apr 14 '13 at 16:19
  • The concept of immutability means that after the object is created, none of its fields may be changed. Since the inner class (`Eagle`) has access to the private instance members of the top-level class (`Animal`), it can change the fields after the object has been instantiated. – Jon Newmuis Apr 21 '13 at 18:16
2

Yes. It is fully immutable. Or to be more precise, its instances are fully immutable.

There can (of course) be subclasses that are not immutable ... but that does not affect the mutability of an instance of the Animal class.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

Writing immutable classes is easy. A class will be immutable if all of the following are true:

1 All of its fields are final
2 The class is declared final
3 The this reference is not allowed to escape during construction
4 Any fields that contain references to mutable objects, such as arrays, collections, or mutable classes like Date:
    4.1 Are private
    4.2 Are never returned or otherwise exposed to callers
    4.3 Are the only reference to the objects that they reference
    4.4 Do not change the state of the referenced objects after construction
Rupok
  • 2,062
  • 16
  • 16
  • 1
    A class will be immutable if all of these conditions are met, but not all of these conditions must be met for a class to be immutable. – Jon Newmuis Nov 02 '11 at 06:51
  • what does the third point mean ? Can you point me to an example where 'this' escapes ? – user2434 Nov 02 '11 at 09:14
  • 1
    @user2434 It means passing the reference to the object being constructed to somewhere outside the constructor. E.g. calling `SomeClass.someMethod(this);`, or `SomeClass.someField = this;` within the constructor. Whoever's receiving that reference could see its state change, as fields haven't been initialised yet. – Grundlefleck Nov 02 '11 at 19:49
2

No, your Animal class is not immutable, because it allows subclassing.

Why not?

See this example subclass:

public class ExceptionalAnimal extends Animal {

    public ExceptionalAnimal() {
        super(null);
    }


    @Override
    public String getName() {
        throw new AssertionError("Oops.. where did that come from?");
    }
}

Why does this matter?

Immutability is commonly used to guarantee:

  1. That the state of an object doesn't change after construction
  2. That objects are thread-safe
  3. That objects behave in a certain way

If a class allows subclassing, none of these guarantees can be relied upon. If you have a method accepting an Animal as a parameter, anyone can pass in a subclass that breaks these guarantees.

Fix: No public or protected constructors

One often used technique is to not have any public or protected constructors. This prevents subclassing from outside your package and inside your package you could still have your own internal subclasses. Which you cannot if the class is final.

The immutable collection classes from Google's Guava library use this technique. From the Javadoc:

Although this class is not final, it cannot be subclassed as it has no public or protected constructors. Thus, instances of this type are guaranteed to be immutable.

A client can create ImmutableLists with the static of() and copyOf methods.

See also Effective Java, Item 4: Enforcing noninstantiability with a private constructor.

Be aware that guaranteeing immutability by having no public or protected constructors is easier to break than making the class final. For example Mockito will let you mock these cases by default:

ImmutableList notSoImmutable = mock(ImmutableList.class)
when(notSoImmutable.size()).thenThrow(new AssertionError("Oops.. where did that come from?"));

On package private classes

As your Animal class is package private it is not possible to create subclasses of it outside of its package. So assuming that in its package you only create subclasses of Animal that respect it's contract, the class is actually immutable.

For my answer I assumed that Animal is public. If you were interested specifically in package private classes, please ignore my answer ;-)

Arend v. Reinersdorff
  • 4,110
  • 2
  • 36
  • 40
  • If the documentation a class specifies that all legitimate derived classes must meet certain criteria, the fact that one could construct a derived class which violates such criteria does not mean that derivatives of the class should not be expected to abide by them. For example, the documentation for `object` specifies that all derivatives should define comparison and hash-code methods such that two instances that compare equal will have the same hashcode. If class `foo` defines its hashcode member to simply return random numbers, storing something in a `dictionary` and... – supercat Mar 14 '12 at 23:21
  • ...attempting to find it would likely fail--not because `dictionary` is broken, but rather because `foo` is broken. If an object's documentation specifies that all legitimate implementations must abide by your criteria for immutability, why should not users of such objects be able to expect that, absent malice, they will abide by them? – supercat Mar 14 '12 at 23:24
  • @supercat the `Animal` class in question has no such documentation. Also, if you make the class `final` you don't have to worry about users not reading the documentation or buggy or malicious subclasses: The compiler guarantees the correct behavior of every variable of type `Animal`. – Arend v. Reinersdorff Mar 15 '12 at 19:33
  • The `Animal` class in question doesn't have *any* documentation. There are many scenarios where it may be useful to have an abstract class or interface and specify certain things about how implementations are supposed to behave. For example, one might define an `Immutable2DArray` interface or abstract class, with the expectation that many implementations would compute values on the fly (one implementation might have the element at `(i,j)` return `(i-500)*(i-500)+(j-500)*(j-500)`). An abstract class couldn't force implementers to be immutable, but could be much more efficient than... – supercat Mar 15 '12 at 19:52
  • ...always having to use a concrete class defined by the creator of the interface, and thus, unless the creator of the interface anticipated what content one would need, end up having to store a large array, rather than some potentially-orders-of-magnitude smaller representation. – supercat Mar 15 '12 at 19:55
  • @Arendv.Reinersdorff just one suggestion, last line of answer should be the first line of your answer to let readers know your context beforehand. Specially when the accepted answer already starts with 'Yes'. In other words, try starting it like, No it is not immutable as your class can be extended by a mutable sub-class... and so on. – sactiw Nov 25 '13 at 11:47
  • @sactiw good point. Added subclassing to first sentence of answer. Thanks. – Arend v. Reinersdorff Nov 30 '13 at 21:22
  • @Arendv.Reinersdorff aren't we talking specifically about Animal class? Jonathan Newmuis had perfectly correct answer. Animal class is effectively immutable, BUT it's descendances may not be immutable. – I4004 Mar 26 '16 at 20:43
  • @I4004 `Animal animal = new ExceptionalAnimal();` Subtypes can always be assigned their Supertypes. Every Animal-typed value could contain an object of a subclass. – Arend v. Reinersdorff Mar 28 '16 at 06:44
  • @Arendv.Reinersdorff We are discussing instances of Animal class, but not its descendants. If you have a reference of type Animal, all behavior will be resolved by the actual instance type - its descendant ExceptionalAnimal. – I4004 Apr 07 '16 at 04:47
0

As long as you have no public setter method as is the case in your example the objects of your class will be immutable.

In Java this may be bypassed using the Reflection API as it allows modification of all kinds of fields.

Putting the final modifier on a class means that it can't be subclassed

Zapodot
  • 462
  • 5
  • 10