[groovy-user] weird ambiguous method problem

2,226 views
Skip to first unread message

OC

unread,
Mar 16, 2014, 4:50:08 PM3/16/14
to user@groovy.codehaus.org User
Hello there,

looks like String/Number are ambiguous, whilst String/Integer are _not_ ambiguous. Does it make sense? How comes?

===
286 /tmp> <q.groovy
static void foo(Integer n) {
println "numeric-foo $n"
}
static void foo(String s) {
println "string-foo $s"
}
[1,'2',null].each { foo(it) } // works perfectly

static void bar(Number n) { // <= only difference, Number vs Integer
println "numeric-bar $n"
}
static void bar(String s) {
println "string-bar $s"
}
[1,'2',null].each { bar(it) } // fails with null
287 /tmp> groovy q
numeric-foo 1
string-foo 2
string-foo null
numeric-bar 1
string-bar 2
Caught: groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method q#bar.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class java.lang.Number]
[class java.lang.String]
...
===

Thanks,
OC


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


Jim White

unread,
Mar 16, 2014, 6:00:41 PM3/16/14
to us...@groovy.codehaus.org
I'm not sure where (or if) the details of the overload decision algorithm are documented.  I'm pretty sure that what you're seeing is the behavior of MetaClassHelper.calculateParameterDistance(Class argument, CachedClass parameter):

MetaClassHelper.java
 
311              // choose the distance to Object if a parameter is null 
312              // this will mean that Object is preferred over a more 
313              // specific type 
314              Class clazz = parameter.getTheClass(); 
315              if (clazz.isPrimitive()) { 
316                  objectDistance += 2; 
317              } else { 
318                  while (clazz != Object.class && clazz != null) { 
319                      clazz = clazz.getSuperclass(); 
320                      objectDistance += 2; 
321                  } 
322              }
I think that you're seeing what's calculated in the loop @ 318. For a null argument (note that the comment is wrong - we get to line 311 when argument == null, not parameter == null) the distances are calculated relative to Object and the distance for String and Number is 2 (a single level) while the distance for Integer is 4 (two levels).

So you're surprised because you expect that Number is getting special treatment, but it is not here.  When the argument value is null here we don't really have enough context to think numbers are special even though there are other places in Groovy where we do indeed do special things for null, strings, and numbers.

Jim

Jochen Theodorou

unread,
Mar 16, 2014, 6:01:53 PM3/16/14
to us...@groovy.codehaus.org
Am 16.03.2014 21:50, schrieb OC:
> Hello there,
>
> looks like String/Number are ambiguous, whilst String/Integer are _not_ ambiguous. Does it make sense? How comes?

so basically the question is about foo(null) with an foo(String) and
foo(Number) being available and why it fails.

Not sure I mentioned it already to you, but in case I did not, I do it
now. Groovy has an "distance" (inheritance wise) based approach. That
means we get the "distance" from the runtime type to the actual type
provided by the method. Normally the method with the smallest distance
wins, but in case of null there is no such type, so we go for the most
general method (smallest distance to Object).

Now looking at String we have Object as direct parent. In case of Number
too. And since interfaces are not used in this case the result is that
both have the same distance and thus the call is ambiguous.

In case of Integer it is not ambiguous, because Integer extends Number,
thus the distance is higher

bye blackdrag

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org

Jim White

unread,
Mar 16, 2014, 6:06:30 PM3/16/14
to us...@groovy.codehaus.org
So if you have a particular protocol you want to follow for null arguments, define an overload that is Object for that parameter.  The distance will always be 0 and therefore always be chosen in that case.

OC

unread,
Mar 16, 2014, 6:42:54 PM3/16/14
to us...@groovy.codehaus.org
Thanks both of you very much for the information!

On 16. 3. 2014, at 23:06, Jim White <james.pa...@gmail.com> wrote:
> So if you have a particular protocol you want to follow for null arguments, define an overload that is Object for that parameter. The distance will always be 0 and therefore always be chosen in that case.

Yup, I've meantime found I can do this:

===
static void foo(Number n) {
println "numeric-foo $n"
}
static void foo(String s) {
println "string-foo $s"
}
static void foo(Object nul) {
println "catches nulls (and other classes which should never happen anyway)"
}
===

and it works perfectly -- nice!

One more question -- from 'foo(Object)', I would need to call specifically 'foo(Number)' with a null argument. I have found that

foo((Number)null)

seems to work as expected (and even 'foo((String)null)' calls the foo(String) properly with a null argument). Is this reliable? It seems to me completely weird that a compile-time typecast should control run-time method calling, but it looks like it indeed does. My question is, can I rely on this trick, or does it just happen to work here and might not elsewhere?

Thanks again,

Jochen Theodorou

unread,
Mar 16, 2014, 6:48:17 PM3/16/14
to us...@groovy.codehaus.org
Am 16.03.2014 23:42, schrieb OC:
[...]
> One more question -- from 'foo(Object)', I would need to call specifically 'foo(Number)' with a null argument. I have found that
>
> foo((Number)null)
>
> seems to work as expected (and even 'foo((String)null)' calls the foo(String) properly with a null argument). Is this reliable? It seems to me completely weird that a compile-time typecast should control run-time method calling, but it looks like it indeed does. My question is, can I rely on this trick, or does it just happen to work here and might not elsewhere?

that is reliable yes. We introduced that, because it is similar to what
you do in Java to force method selection and because you have to have a
way to resolve problematic overloads.

bye blackdrag

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org


Reply all
Reply to author
Forward
0 new messages