Standard Analyses

SemmleCode Professional comes with a wide range of pre-packaged analyses, from architectural properties and metrics, to statement-level checks for likely bugs and violations of best practice.


Missing Definition of Equals

This query finds classes that define fields but do not provide a custom definition of equals, instead inheriting it from a superclass. The presence of the extra fields is thus ignored in equality tests.

Consider the following two example classes:

    import java.awt.Color;

    class Point {
        private double x, y;

        public Point(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public boolean equals(Object o) {
            if(!(o instanceof Point))
                return false;
            Point p = (Point)o;
            return x == p.x && y == p.y;
        }
    }

    class ColoredPoint extends Point {
        private Color color;

        public Point(double x, double y, Color color) {
            super(x, y);
            this.color = color;
        }
    }

Class Point provides a custom equality method that compares points coordinate-wise. Class ColoredPoint does not override this behaviour. Given the definitions

    Point p = new ColoredPoint(23, 42, Color.BLACK);
    Point q = new ColoredPoint(23, 42, Color.WHITE);    

the expression p.equals(q) evaluates to true, i.e. the two points are equal although their colors are different.

How to Interpret the Query Results

The query flags all such classes and provides a list of detected occurrences in the results view.

How to Address the Query Results

Provide a custom implementation of equals. For ColoredPoint above, one could use

        public boolean equals(Object o) {
            if(!super.equals(o))
                return false;
            if(!(o instanceof ColoredPoint))
                return false;
            ColoredPoint cp = (ColoredPoint)o;
            return color == cp.color;
        }
Source Code
import default

from Class c
where not exists(EqualsMethod m| m.getDeclaringType() = c) and
      exists(EqualsMethod m| c.inherits(m) and 
                             not m.getDeclaringType() instanceof Interface and
                             not m.getDeclaringType() instanceof TypeObject) and
      exists(Field f | f.getDeclaringType() = c) and
      c.fromSource() and
      not c instanceof EnumType
select c, "Class doesn't override equals in superclass"
References