Immutable Enforcement with AspectJ

Java has no built-in immutability enforcement. You can mark fields final, but the compiler won’t stop you putting a mutable object in one. I wrote an AspectJ aspect that actually enforces it.

The Approach

Classes annotated with @Immutable get validated at load time. The aspect checks two rules:

  • Primitive fields must be declared final
  • Object fields must themselves be typed as @Immutable classes

To avoid annotating everything, I maintain a trusted list of known-immutable types — String, Float, and so on — that are treated as implicitly immutable.

The Annotation

Existing JSR annotations at the time only supported type-level annotation, not field-level. I wrote a custom @Immutable annotation applicable to both:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface Immutable {}

The Aspect

The aspect uses pointcuts to intercept field mutations and validates the two rules above. A valid class looks like:

@Immutable
public class Point {
    private final int x;
    private final int y;
    private final String label; // trusted immutable type
}

Violations — a non-final field, or a field typed as a mutable class — throw at load time rather than silently allowing broken immutability assumptions.

Limitations

I didn’t finish extending enforcement to superclasses. A complete implementation would also require that any superclass either carries @Immutable or is Object directly — otherwise a subclass could inherit mutable fields from an unannotated parent. That remains an open gap.