2016-04-03 13 views
6

Ricevo un errore del compilatore "fare riferimento a rendere ambiguo" che non capisco.Dov'è l'ambiguità in questa chiamata al metodo Java?

ho questi due metodi

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 

Questa riga di codice viene contrassegnato come ambigua

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

Ecco l'errore

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...) 

Non ritiene la presenza del il parametro booleano rende il primo metodo più simile al secondo?

Se è importante, questo è parte di un test PowerMock Ecco il metodo completo

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 

}

Se aiuta: sto usando javac 1.8.0_77, Mokito 1.10.19, e Powermock 1.6.3.

+0

Sulla base delle soluzioni di seguito, ho rivolto gli errori del compilatore modificando il parametro 'boolean' a' boolean'. Ciò significa che gli utenti devono passare 'Boolean.TRUE' invece di' true'; ma, sembra meno fastidioso che aver loro digitare 'new Object [] {a, b, c}' – Zack

risposta

5

Il problema sta nel

Object... params 

Quando si chiama SimpleFactory.make(className, Object.class, false, one, two); Java non sapranno se la casella "false" in un oggetto booleano e farlo passare come primo argomento delle "params" varargs array (booleano estende Object) e utilizzare

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

o se chiamare

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

poiché tale firma può anche accettare un valore booleano prima dei parametri varargs.

Quindi perché è ambiguo, sono applicabili entrambe le firme dei metodi.

+0

Penso che valga la pena notare che l'ambiguità arriva perché Java classifica ugualmente varargs e autoboxing. La maggior parte di noi si aspetterebbe l'uso di un booleano letterale per fare della scelta 1 una corrispondenza migliore; ma, poiché l'autoboxing è sullo stesso "livello", Java non sceglierà. – Zack

+0

Questa è una scelta sbagliata IMO. La chiamata con il valore booleano dovrebbe essere preferita. – Vincent

1

Penso che sia perché il compilatore non sa se si desidera che il parametro booleano sia incluso in params o meno. Potrei chiamare la funzione con 4 parametri e passare il valore booleano come terzo parametro oppure chiamare la funzione con 3 parametri e aggiungere il valore booleano ai parametri dell'oggetto .... Il compilatore non sa cosa fare a causa di questa ambiguità. Fatemi sapere se avete bisogno di ulteriori informazioni

6

Il compilatore prima cerca di trovare una firma corrispondente non coinvolge autoboxing/unboxing o invocazione variabile arity. L'invocazione dell'arit a variabile si ha quando si richiama un metodo varargs passando un elenco di parametri come ultimo argomento (al contrario di un array).

Nel tuo caso, entrambi implicano l'invocazione di una variabile. Quando ciò accade, viene scelto il sovraccarico più specifico. Per la tua situazione, nessuno dei due è considerato più specifico come definito nel JLS. Questo è essenzialmente perché nessuno dei tipi boolean e Object è un sottotipo dell'altro.

Semplificando un po 'l'esempio, quanto segue non viene compilato.

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

} 

static void foo(Object... arr) { 

} 

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

La prima versione non accetterà un solo argomento di tipo Object e il secondo non accetterà un solo argomento di tipo boolean. Quindi nessuno dei due è più specifico. (Autoboxing rende solo look come se fosse possibile passare un boolean come argomento di tipo Object).

D'altra parte, se si sostituisce boolean da Boolean, non compilato perché Boolean è un sottotipo di Object.

+0

Questa risposta non spiega perché 'make (Classe stringa , booleana, Oggetto ...)' non sarà selezionato per 'make (className, Object.class, false, one, two) 'poiché questo sarebbe il più specifico. E il codice OP funziona bene per me. – Tom

+0

@ Tom Non so cosa intendi. La mia risposta spiega perché nessuno dei due è più specifico, vero? Se il codice viene compilato per te, si tratta di un bug del compilatore. –

+0

@Tom http://ideone.com/cAGsYR –

1

problema si trova con

Object... params 

Per risolvere l'ambiguità - modificare il codice come qui di seguito

Per chiamare

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

chiamare in questo modo:

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

E

Per chiamare

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

chiamata in questo modo:

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

@ Tom: Grazie per averlo preso. Aggiornato il post. – Sanj

+1

Che funziona; ma l'intento delle vararg è di rendere i metodi più facili per gli utenti. Dover avvolgere le varargs sconfigge quello scopo. Nel mio caso, la modifica di 'boolean' in' Boolean' risolve il problema con meno "digitazioni extra" – Zack

Problemi correlati