Discussing the design, decisions and discovery involved in developing Mutability Detector, an open-source analysis tool for Java.

Sunday, 4 July 2010

An Introduction To Mutability Detector (Part 1 of n)

(n still to be decided)

Mutability Detector is a tool which analyses Java code, and tells you if instances of a given class are immutable or mutable. Having been a google code project for around six months, some recent discussions (one on the issues page, and another in the mailing list for Project Lombok, in an unrelated discussion) have brought a little bit of attention to the project. I thought it would be useful to give an overview of the usage of Mutability Detector, and how it can be used to recognise mutability in your classes.

Mutability Detector works by applying a set of checkers to each class. Each checker looks for a particular way of rendering a class as mutable, and the results from each checker are combined to give the overall "mutability result" of the class. The checkers closely relate to a set of rules for achieving immutability. These began as the rules defined by Josh Bloch in the excellent Effective Java, 2nd Edition. Josh lists five rules (in Item 15), these are:

1. Don’t provide any methods that modify the object’s state (known as mutators).
2. Ensure that the class can’t be extended.
3. Make all fields final.
4. Make all fields private.
5. Ensure exclusive access to any mutable components.

These formed a good basis for an everyday tool for ensuring your classes are immutable. But I found them to be a bit on the strict side (more on that in a later blog entry). When first writing the tool, those rules were combined with the following:

1. It should not be possible to reassign any field. (this is almost implicit in Josh's rules 1, 3 & 4)
2. An immutable object should consist of immutable fields.
This implies three sub-rules. If a field is not constructed directly (i.e. passed as a parameter, or as the result of a method call):
i. It must not be possible to subclass the type.
ii. It should be declared as a concrete type.
iii. If the previous two cannot be achieved, make a copy of in the constructor and assign that to the field.

Some of these rules are pretty opaque for understanding what the tool actually detects (and in the past few days, I've found them to be a bit lacking). What might help understand Mutability Detector is some of the patterns of code which are detected and reported. Listed here is each checker, some example code and the results of applying the checker to that code.


FinalClassChecker
public class MutableByNotBeingFinalClass {

}
MutableByNotBeingFinalClass is MAYBE immutable
Is not declared final, and thus may be immutable.

PublishedNonFinalFieldChecker

public class MutableByHavingPublicNonFinalField {
public String name;

public MutableByHavingPublicNonFinalField(String name) {
this.name = name;
}
}
MutableByHavingPublicNonFinalField is DEFINITELY_NOT immutable
Field [name] is visible outwith this class, and is not declared final.

SetterMethodChecker

public final class MutableByHavingSetterMethod {
private String name;
public void setName(String name) {
this.name = name;
}
}
MutableByHavingSetterMethod is DEFINITELY_NOT immutable
Field [name] can be reassigned within method [setName]
AbstractTypeToFieldChecker
public final class MutableByAssigningAbstractTypeToField {
private AbstractStringContainer nameContainer;

public MutableByAssigningAbstractTypeToField(AbstractStringContainer abstractNameContainer) {
nameContainer = abstractNameContainer;
}

abstract class AbstractStringContainer {
protected final String name = "my name";
}
final class StringContainer extends AbstractStringContainer {
public String someMutableField;
}
}
MutableByAssigningAbstractTypeToField is DEFINITELY_NOT immutable
Field [nameContainer] can have an abstract type (AbstractStringContainer) assigned to it.


(Here the problem is, that while AbstractStringContainer is immutable, StringContainer is not, and either could be passed as a parameter to the constructor. Thus we can never be sure that the field we're assigning to references an immutable object or not. Though this could be relaxed to render types like these as MAYBE immutable.)

InherentTypeMutabilityChecker

This checker is used more to help decide the immutability status for client classes. For example, if a class has a field which is inherently mutable, this affects the containing class.

public class MutableByHavingArrayTypeAsField {
private final String names[];
}
MutableByHavingArrayTypeAsField is DEFINITELY_NOT immutable
Field [names] is a primitive array.

This currently quite limiting - the field 'names' may not actually be mutated, yet the containing class would be declared as mutable. This is definitely something to work on.


Some of these rules aren't complete. Sometimes they don't interact too well with certain classes (inheritance is hairy issue). Common patterns, such as lazily loaded fields, aren't handled at all (thus java.lang.String is supposedly mutable). So there's a lot to do, but hopefully this entry has provided a decent introduction to Mutability Detector.

No comments:

Post a Comment