Groovy: java.lang.StackOverflowError When Implementing equals()

PROBLEM

While migrating a portion of my Java code to Groovy code, I got bitten by the Groovy operator loading feature that I should have known better… and my pride hurts, but hey, I admit I write shitty code.

Consider this simple POGO with custom equals() and hashCode(), both implemented using Google Guava libraries:-

@Canonical
class Person {
    String firstName
    String lastName
    String email

    @Override
    boolean equals(Object o) {
        if (this == o) {
            return true
        }
        if (o == null || getClass() != o.getClass()) {
            return false
        }

        final Person other = (Person) o

        return Objects.equal(email, other.email)
    }

    @Override
    int hashCode() {
        return Objects.hashCode(email)
    }
}

What is wrong with the above code? Well, if you are mostly a Java developer like me, this look pretty much correct. However, when I perform an equality check, I get java.lang.StackOverflowError exception. I tend to see this exception when I write my too-smart-for-production recursion API that couldn’t seem find its way to end the recursion, causing the JVM stack to blow up.

SOLUTION

The reason we are getting java.lang.StackOverflowError exception is because Groovy overloads == with equals(). So, if (this == o) { ... } becomes if (this.equals(o)) { ... }. When we perform an equality check, it will call itself again and again until it chokes itself and dies.

To fix this, we have to use if (this.is(o)) { ... } to perform an identity check:-

@Canonical
class Person {
    String firstName
    String lastName
    String email

    @Override
    boolean equals(Object o) {
        if (this.is(o)) {
            return true
        }
        if (o == null || getClass() != o.getClass()) {
            return false
        }

        final Person other = (Person) o

        return Objects.equal(email, other.email)
    }

    @Override
    int hashCode() {
        return Objects.hashCode(email)
    }
}

Advertisements

2 thoughts on “Groovy: java.lang.StackOverflowError When Implementing equals()

  1. selimok

    Thank you for your blog entry. It inspired me to finding solution for my problem.

    I got the same StackOverflowError while calling hashCode of my entity class. I used guava Objects’ hashCode implementation. The hashCode method itself is generated by eclipse guava plugin and the method call includes a class member which is recursively reference the class itself again. In this case hashCode implementation go through this cyclic dependency and calls again and again itself. So I learned, i must be careful using auto generated hashCode or toString methods, especially by entity objects which reference another object with reference back to this entity.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s