2. Usage2. Usage2.2. Complaints
PreviousUpNext

Annotations
Overview
In Java, if a class does not override the equals method provided by the standard java.lang.Object type, then the class effectively has reference equality. That is, the equals method simply compares the addresses of the operands. In some cases this is desirable; in most cases it is not. It is very easy to forget to override an equals method, leading to extremely subtle and dangerous bugs.
The com.io7m.jequality package provides a set of annotations and a validator to check that classes conform to their annotations. Essentially, the programmer annotates a class with an annotation that states that the class is expected to implement either structural or reference equality. A class is assumed to implement structural equality iff it overrides equals. The provided validator checks that classes are annotated, and that they implement the type of equality that their annotations claim. As an extra safety check, the validator checks that if equals is overridden, then hashCode is also overridden in the same class. The validator is not capable of checking that an overridden equals actually does implement structural equality (and does not, for example, simply delegate to java.lang.Object). Solving this problem is undecidable in general.
Validation
Validation of classes is expected to occur in the test suite of the project using the com.io7m.jequality package.
As an example:
@EqualityReference public final class RefEquality
{

}

public final class RefEqualityTests
{
  @Test public void testEquality()
  {
    Assert.assertEquals(ValidatorResult.VALIDATION_OK, EqualityValidator
      .validateClass(
        RefEquality.class,
        AnnotationRequirement.ANNOTATIONS_REQUIRED,
        true));
  }
}
The RefEquality class uses the EqualityReference annotation to indicate that that it implements reference equality. The validator checks that this is indeed the case. The following test fails:
@EqualityReference public final class RefEqualityWrong
{
  @Override public boolean equals(
    final @Nullable Object other)
  {
    return false;
  }

  @Override public int hashCode()
  {
    return 0;
  }
}

public final class RefEqualityTests
{
  @Test public void testReferenceEqualityNot()
  {
    Assert.assertEquals(
      ValidatorResult.VALIDATION_ERROR_WANTED_REFERENCE_EQUALITY,
      EqualityValidator.validateClass(
        RefEqualityWrong.class,
        AnnotationRequirement.ANNOTATIONS_REQUIRED,
        true));
  }
}
The RefEqualityWrong class claims that it implements reference equality, and yet overrides equals. The validator will catch this and the test will fail.
@EqualityStructural public final class StructuralEquality
{
  @Override public boolean equals(
    final @Nullable Object other)
  {
    return false;
  }

  @Override public int hashCode()
  {
    return 0;
  }
}

public final class RefEqualityTests
{
  @Test public void testStructuralEquality()
  {
    Assert.assertEquals(ValidatorResult.VALIDATION_OK, EqualityValidator
      .validateClass(
        StructuralEquality.class,
        AnnotationRequirement.ANNOTATIONS_REQUIRED,
        true));
  }
}
The StructuralEquality class claims to implement structural equality and does appear to override equals (even though the equality relation it defines is not correct). The validator assumes that this class is correct.

PreviousUpNext
2. Usage2. Usage2.2. Complaints