Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy: same parameters, different results

Tags:

groovy

In the below code, x.test() returns [1,2].

So y = [1,2].

Yet f([1,2]) prints 1, but f(y) prints 2.

How do I write f(y) so it prints 1?

Perversely, f(z) prints 1, even though z = y.

def f = { Object... args -> println args.size(); };

class Test { Object[] test() { return [1,2]; } }

def x = new Test();
def y = x.test();
def z = [1,2];

f([1,2]); // 1
f(y); // 2
f(z); // 1
like image 207
Clinton Avatar asked Jun 03 '13 06:06

Clinton


People also ask

Does == work in Groovy?

== is symmetric in groovy. While that table is super handy, it's a bit misleading as == isn't actually on it.

How do I use not equal in Groovy?

In groovy, the ==~ operator (aka the "match" operator) is used for regular expression matching. != is just a plain old regular "not equals".

How do I compare two objects in Groovy?

In Groovy we use the == operator to see if two objects are the same, in Java we would use the equals() method for this. To test if two variables are referring to the same object instance in Groovy we use the is() method. The !=


2 Answers

The problem is that y and z, while they appear to be the same, are actually of different types. y is an Object[] while z is an ArrayList<Integer>. Groovy handles arrays and lists differently, automatically coercing the former into a varargs parameter list, but not the latter.

println y.getClass(); // class [Ljava.lang.Object
println z.getClass(); // class java.util.ArrayList

As for a solution to your problem, either change your test() to return a List instead of an array:

class Test { List test() { return [1,2]; } }

or manually coerce the array into a list when you pass it to f:

f(y as List); // 1
like image 79
Chris B Avatar answered Nov 15 '22 11:11

Chris B


The expression [1,2] in Groovy denotes an ArrayList with two members, Integer.valueOf(1) and Integer.valueOf(2). Thus when you call f([1,2]) Groovy creates a single-element array containing this ArrayList as its only item, and passes that array as the closure argument.

But x.test() is declared to return Object[] so the [1,2] ArrayList will be converted to a two element Object[] by the return. Thus y is already an Object[] and does not need to be boxed up in a varargs array to be passed to f.

You need to turn y back into a list, either by changing the return type of test() or by saying

f(y as List)

Conversely, you can use the spread operator

f(*z) // 2

which will extract the elements of the ArrayList and pass them as individual arguments to the call (which will then be packaged up into a varargs array as usual).

like image 38
Ian Roberts Avatar answered Nov 15 '22 10:11

Ian Roberts