6
votes

I am working under DSL using Groovy categories and I need to override/overload == operator. It is however known issue, that when class implements Comparable, Groovy will call compareTo() method for == operator. I'm looking for some workaround (not AST transformation) in order to make == do exactly what I want.

I have the following "toy" situation:

class Base implements Comparable<Base>{
    int a, b

    Base(int a, int b) {
        this.a = a
        this.b = b
    }

    @Override
    int compareTo(Base o) {
        return a <=> o.a //compare only a
    }
}

class BaseCategory {
    static boolean equals(Base base, o) { //complete equals
        if (o == null)
            throw new NullPointerException()
        if (o.getClass() != base.getClass())
            return false
        return base.a == o.a && base.b == o.b
    }

    static int compareTo(Base base, o) { //compatible with equals
        int c = base.a <=> o.a;
        if (c != 0)
            return c
        return base.b <=> o.b;
    }
}

Now when I run

use(BaseCategory) {
    Base a = new Base(1, 2)
    Base b = new Base(1, 3)
    println a == b
    println a <=> b
}

I got true and 0, instead of false and -1. Is there any workaround?

1
Your equals method calls your hashCode method directly, which is not a great idea, but may not be the problem you are concerned with. The hashCode method method is just returning 2 * a so b is irrelevant. Is that really what you want? - Jeff Scott Brown
This is just for illustration. I've edited example to make more meaningful. - Stanislav Poslavsky
i don't see a use block or a mixin, how does the category get applied here? - Nathan Hughes
@NathanHughes I've corrected example - Stanislav Poslavsky
@MikeSamuel these are minor issues which are not relevant for this "toy" example. Even if I'll implement equals() and compareTo() more carefully, the result will be the same. - Stanislav Poslavsky

1 Answers

5
votes

An old Groovy bug[1][2] to be fixed in 3.0. Does your use case permit encapsulation and delegation?

class Base implements Comparable<Base>{
    int a, b

    @Override int compareTo(Base o) {
        return a <=> o.a
    }
}

class BaseDelegate {
    @Delegate Base base

    boolean equals(o) {
        if (o == null)
            throw new NullPointerException()
        if (o.getClass() != base.getClass())
            return false
        return base.a == o.a && base.b == o.b
    }

    int compareTo(o) { 
        int c = base.a <=> o.a;
        if (c != 0)
            return c
        return base.b <=> o.b;
    }
}

And the tests:

def a = new Base(a: 1, b: 2)
def b = new Base(a: 1, b: 3)

assert (a == b) == true
assert (a <=> b) == 0


def a1 = new BaseDelegate(base: a)
def b1 = new BaseDelegate(base: b)

assert (a1 == b1) == false
assert (a1 <=> b1) == -1