Domanda

Oggi ho notato che l'auto-boxing a volte può causare ambiguità nella risoluzione del sovraccarico del metodo. L'esempio più semplice sembra essere questo:

public class Test {
    static void f(Object a, boolean b) {}
    static void f(Object a, Object b) {}

    static void m(int a, boolean b) { f(a,b); }
}

Quando compilato, provoca il seguente errore:

Test.java:5: reference to f is ambiguous, both method
    f(java.lang.Object,boolean) in Test and method
    f(java.lang.Object,java.lang.Object) in Test match

static void m(int a, boolean b) { f(a, b); }
                                  ^

La correzione di questo errore è banale: basta usare il boxing esplicito:

static void m(int a, boolean b) { f((Object)a, b); }

Che chiama correttamente il primo sovraccarico come previsto.

Quindi perché la risoluzione del sovraccarico non è riuscita? Perché il compilatore non ha auto-boxato il primo argomento e ha accettato normalmente il secondo argomento? Perché ho dovuto richiedere il boxing esplicito?

È stato utile?

Soluzione

Quando lanci tu stesso il primo argomento su Object, il compilatore abbinerà il metodo senza usare l'autoboxing (JLS3 15.12.2):

  

Viene eseguita la prima fase (& # 167; 15.12.2.2)   risoluzione del sovraccarico senza permesso   conversione di boxe o unboxing o   uso del metodo dell'arity variabile   invocazione. Se non esiste un metodo applicabile   trovato in questa fase quindi   l'elaborazione continua al secondo   fase.

Se non lo lanci in modo esplicito, passerà alla seconda fase del tentativo di trovare un metodo di corrispondenza, consentendo l'autoboxing, quindi è davvero ambiguo, perché il tuo secondo argomento può essere abbinato da booleano o Object.

  

Viene eseguita la seconda fase (& # 167; 15.12.2.3)   risoluzione del sovraccarico permettendo   boxe e unboxing, ma comunque   preclude l'uso di arity variabile   invocazione del metodo.

Perché, nella seconda fase, il compilatore non sceglie il secondo metodo perché non è necessario il boxing automatico dell'argomento booleano? Perché dopo aver trovato i due metodi di corrispondenza, viene utilizzata solo la conversione del sottotipo per determinare il metodo più specifico dei due, indipendentemente da qualsiasi box o unboxing che ha avuto luogo per abbinarli in primo luogo (& # 167; 15.12. 2.5).

Inoltre: il compilatore non può sempre scegliere il metodo più specifico in base al numero di box (un) automatici necessari. Può comunque provocare casi ambigui. Ad esempio, questo è ancora ambiguo:

public class Test {
    static void f(Object a, boolean b) {}
    static void f(int a, Object b) {}

    static void m(int a, boolean b) { f(a, b); } // ambiguous
}

Ricorda che l'algoritmo per la scelta di un metodo di abbinamento (fase di compilazione 2) è fisso e descritto in JLS. Una volta nella fase 2 non c'è autoboxing selettivo o unboxing. Il compilatore individuerà tutti i metodi che sono accessibili (entrambi i metodi in questi casi) e applicabili (di nuovo i due metodi), e solo allora sceglie quello più specifico senza guardare boxing / unboxing, che è ambiguo qui.

Altri suggerimenti

Il compilatore ha auto-boxato il primo argomento. Una volta fatto, è il secondo argomento che è ambiguo, in quanto potrebbe essere visto come booleano o come oggetto.

Questa pagina spiega le regole per l'autoboxing e selezionare quale metodo invocare. Il compilatore tenta innanzitutto di selezionare un metodo senza utilizzare alcun autoboxing , poiché boxe e unboxing comportano penali di prestazione. Se nessun metodo può essere selezionato senza ricorrere al pugilato, come in questo caso, il pugilato è sulla tabella per tutti argomenti per quel metodo.

Quando dici f (a, b ), il compilatore è confuso su quale funzione dovrebbe fare riferimento.

Questo perché a è un int , ma l'argomento atteso in f è un oggetto. Quindi il compilatore decide di convertire a in un oggetto. Ora il problema è che, se a può essere convertito in un oggetto, quindi può essere b .

Ciò significa che la chiamata di funzione può fare riferimento a entrambe le definizioni. Questo rende la chiamata ambigua.

Quando converti un in un oggetto manualmente, il compilatore cerca solo la corrispondenza più vicina e quindi fa riferimento ad essa.

  

Perché il compilatore non ha selezionato il   funzione che può essere raggiunta facendo " doing   il minor numero possibile di   conversioni di boxe / unboxing " ;?

Vedi il seguente caso:

f(boolean a, Object b)
f(Object a , boolean b)

Se chiamiamo come f (booleano a, booleano b) , quale funzione dovrebbe selezionare? È ambiguo vero? Allo stesso modo, questo diventerà più complesso quando saranno presenti molti argomenti. Quindi il compilatore ha scelto di avvisarti invece.

Dato che non c'è modo di sapere quale delle funzioni che il programmatore intendeva chiamare, il compilatore dà un errore.

  

Allora perché ha fatto la risoluzione del sovraccarico   fallire? Perché la casella automatica del compilatore non è stata eseguita   il primo argomento, e accetta il   secondo argomento normalmente? Perché l'ho fatto   è necessario richiedere il boxing automatico   esplicitamente?

Normalmente non ha accettato il secondo argomento. Ricorda che & Quot; booleano & Quot; può anche essere inscatolato in un oggetto. Avresti potuto esplicitamente lanciare anche l'argomento booleano su Object e avrebbe funzionato.

Vedi http: //java.sun. com / docs / libri / JLS / third_edition / html / expressions.html # 20448

Il cast aiuta perché non è necessario il pugilato per trovare il metodo da chiamare. Senza il cast il secondo tentativo è quello di consentire la boxe e quindi anche il booleano può essere inscatolato.

È meglio avere specifiche chiare e comprensibili per dire cosa accadrà piuttosto che indurre le persone a indovinare.

Il compilatore Java risolve i metodi e i costruttori sovraccaricati in fasi. Nella prima fase [& # 167; 15.12.2.2], identifica i metodi applicabili sottotipizzando [& # 167; 4.10]. In questo esempio, nessuno dei due metodi è applicabile, poiché int non è un sottotipo di Object.

Nella seconda fase [& # 167; 15.12.2.3], il compilatore identifica i metodi applicabili mediante la conversione dell'invocazione del metodo [& # 167; 5.3], che è una combinazione di autoboxing e sottotipizzazione. L'argomento int può essere convertito in un numero intero, che è un sottotipo di Object, per entrambi i sovraccarichi. L'argomento booleano non necessita di conversione per il primo sovraccarico e può essere convertito in booleano, un sottotipo di Object, per il secondo. Pertanto, entrambi i metodi sono applicabili nella seconda fase.

Poiché è applicabile più di un metodo, il compilatore deve determinare qual è il più specifico [& # 167; 15.12.2.5]. Confronta i tipi di parametro, non i tipi di argomento e non li boxa automaticamente. Oggetto e valore booleano sono tipi non correlati, quindi sono considerati ugualmente specifici. Nessuno dei due metodi è più specifico dell'altro, quindi la chiamata al metodo è ambigua.

Un modo per risolvere l'ambiguità sarebbe quello di cambiare il parametro booleano per digitare Boolean, che è un sottotipo di Object. Il primo sovraccarico sarebbe sempre più specifico (se applicabile) rispetto al secondo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top