Groovy 3.0 breaking changes

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

Groovy 3.0 breaking changes

Paolo Di Tommaso
Dear all, 

Currently Groovy breaks the polymorphism contract for the `equals` and `toString` methods for objects implementing List, Set, Map interfaces. For example: 

class Mylist extends ArrayList {
  Mylist(Collection c) { super(c) } 
  @Override boolean equals(Object o) { throw new UnsupportedOperationException () }
  @Override int hashCode() { throw new UnsupportedOperationException () }
  @Override String toString() { return 'CUSTOM STRING' }

def l = new Mylist([1,2,3]) 
assert l.toString() == 'CUSTOM STRING'
assert "$l" == '[1, 2, 3]'

def q = new Mylist([1,2,3])
assert l.equals(q)
assert l == q 

In the above snippet the `toString` is *not* invoked when interpolated in a GString, also  the `equals` method not invoked, not even when it's explicitly referenced.  

A similar problem for the `equals` method exists when a class implements the `Comparable` interface. For example: 

class Foo implements Comparable<Foo> {
  private int x
  Foo(int x) { this.x=x }   
  @Override int compareTo(Foo o) { throw new IllegalArgumentException('SHOULD INVOKE EQUALS!') }
  @Override  boolean equals(Object o) { return this.x == o.x }

assert new Foo(1).equals(new Foo(1))  // OK 
assert new Foo(1) == new Foo(1)       // throws IllegalArgumentException: SHOULD INVOKE EQUALS!

In this case the `==` operator uses `compareTo` instead of the `equals` method.

I know there are historical reasons behind each of them, however they represent really nasty inconstancies and cause of a lot of problems. I hope that Groovy 3.0 is planning to solve these problems or at least give a mechanism to implement a proper behaviour.