Question

What is the best way in Java to enforce class instance invariants (i.e. to ensure that certain statements are true right before and after calling any public method)?

I will include below an example (the one that has made me wonder about this question), but I am in fact more interested in a general solution for imposing class-instance invariants.

Let's say I have the following classes:

public class Constraint
{
  private int cardinality;

  // constructor and getter/setter omitted
}

public class Edge
{
  private int minCardinality = 1;
  private int maxCardinality = Integer.MAX_VALUE;
  private Constraint constraint = null;

  // constructors, getters/setters and other methods omitted
}

and that I'd like to enforce the following statements on the Edge class:

  • 0 <= minCardinality <= maxCardinality
  • if (constraint != null) then (minCardinality <= constraint.getCardinality() <= maxCardinality)

Here Edge instances are created by a parser with (obligatory) attributes minCardinality and maxCardinality set. The (optional) constraint attribute is left blank. The instances are then handed over to another module (a test frame generator) which might or might not set the constraint via a setter method.

Was it helpful?

Solution

Use a builder and put the checks in it; this way your Edge class does not care:

@Immutable // See JSR 305
public final class Edge
{
    private final int minCardinality;
    // ...

    public static Builder newBuilder() { return new Builder(); }

    // Edge has no public constructor anymore, only this one:
    private Edge(final Builder builder)
    {
        minCardinality = builder.minCardinality;
        // etc
    }

    @NotThreadSafe // See JSR 305
    public static final class Builder
    {
        private int minCardinality;
        // ...

        private Builder() {}

        public Builder withMinCardinality(final int minCardinality)
        {
            this.minCardinality = minCardinality;
            return this;
        }
        // etc

        public Edge build()
        {
            // checks here
            return new Edge(this);
        }
    }
}

Usage:

final Edge edge = Edge.newBuilder().withMinCardinality(xxx).etc().etc().build();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top