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:
- That the state of an object doesn't change after construction
- That objects are thread-safe
- 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 ImmutableList
s 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 ;-)