Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where is the ambiguity in this Java method call?

I am getting a "reference to make is ambiguous" compiler error that I don't understand.

I have these two methods

public static <T> T make(String name, Class<T> parentClass, 
                         boolean rethrowRuntimeExceptions, 
                         Object... params) throws DLException

 public static <T> T make(String name, Class<T> parentClass,
                          Object... params) throws DLException

This line of code is being marked as ambiguous

  String className = "clsNme";
  String one = "1";
  String two = "2";     
  SimpleFactory.make(className, Object.class, false, one, two);

Here is the error

both method <T#1>make(String,Class<T#1>,boolean,Object...) in SimpleFactory and method <T#2>make(String,Class<T#2>,Object...) in SimpleFactory match
    [javac]   where T#1,T#2 are type-variables:
    [javac]     T#1 extends Object declared in method <T#1>make(String,Class<T#1>,boolean,Object...)
    [javac]     T#2 extends Object declared in method <T#2>make(String,Class<T#2>,Object...)

Doesn't the presence of the boolean parameter make the first method a closer match than the second?

If it matters, this is part of a PowerMock test Here is the complete method

public void makeCallsMakeWithFalse() throws Throwable {
  Object expected = mock(Object.class);
  String className = "clsNme";

  String one = "1";
  String two = "2";

  spy(SimpleFactory.class);

  doReturn(expected).when(SimpleFactory.class);
  SimpleFactory.make(className, Object.class, false, one, two);  // causes error

  Object observed = SimpleFactory.make(className, Object.class, one, two); // doesn't cause error
  assertEquals(expected, observed);

  verifyStatic();
  SimpleFactory.make(className, Object.class, false, one, two);  // causes error

}

If it helps: I'm using javac 1.8.0_77, Mokito 1.10.19, and Powermock 1.6.3.

like image 838
Zack Avatar asked Apr 03 '16 00:04

Zack


4 Answers

The compiler first tries to find a matching signature thst does not involve autoboxing/unboxing or variable arity invocation. Variable arity invocation is when you invoke a varargs method by passing a parameter list as the last argument (as opposed to an array).

In your case, both involve variable arity invocation. When this happens, the most specific overload is chosen. For your situation, neither is considered more specific as defined in the JLS. This is essentially because neither of the types boolean and Object is a subtype of the other.

Simplifying your example a bit, the following does not compile.

static void foo(boolean b, Object... arr) {

}

static void foo(Object... arr) {

}

public static void main(String[] args) {
    foo(true);
}

The first version will not accept a single argument of type Object and the second will not accept a single argument of type boolean. Therefore neither is more specific. (Autoboxing only makes it look as if you can pass a boolean as an argument of type Object).

On the other hand, if you replace boolean by Boolean, it does compile because Boolean is a subtype of Object.

like image 170
Paul Boddington Avatar answered Nov 15 '22 01:11

Paul Boddington


The problem lies in

Object... params

When calling SimpleFactory.make(className, Object.class, false, one, two); Java will not know whether to box the "false" into a Boolean object and pass it as the first argument of the "params" varargs array (Boolean extends Object) and use

make(String name, Class<T> parentClass, Object... params)

or whether to call

make(String name, Class<T> parentClass, boolean rethrowRuntimeExceptions, Object... params)

since that signature can also accept a boolean before the params varargs.

Hence why it's ambiguous, both method signatures are applicable.

like image 44
mmaarouf Avatar answered Nov 15 '22 00:11

mmaarouf


I think it is because the compiler doesn't know if you want the boolean parameter to be included in params or not. I could either call the function with 4 parameters and pass the boolean in as the 3rd parameter or it could call the function with 3 parameters and add the boolean to the Object... params. The compiler doesn't know what to do because of this ambiguity. Let me know if you need more information

like image 39
Jeremy Hanlon Avatar answered Nov 14 '22 23:11

Jeremy Hanlon


Problem lies with

Object... params 

To fix the ambiguity - change the code like below

To call

public static <T> T make(String name, Class<T> parentClass, 
                         boolean rethrowRuntimeExceptions, 
                         Object... params) throws DLException

call it like this:

  SimpleFactory.make(className, Object.class, false, new Object[]{one, two});

And

To call

 public static <T> T make(String name, Class<T> parentClass,
                          Object... params) throws DLException

call it like this:

  SimpleFactory.make(className, Object.class, new Object[]{false,one, two});
like image 44
Sanj Avatar answered Nov 14 '22 23:11

Sanj