Promozione del tipo Java nei parametri


20

Mi sono imbattuto in questo frammento:

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Ciò comporterà un errore di compilazione:

Errore: (15, 9) java: il riferimento a printSum è ambiguo sia nel metodo printSum (int, double) in ParamTest che nel metodo printSum (long, long) nella corrispondenza ParamTest

Com'è ambiguo? In questo caso non dovrebbe essere promosso solo il secondo parametro poiché il primo parametro è già un int? In questo caso non è necessario promuovere il primo param, giusto?

La compilazione ha esito positivo se aggiorno il codice per aggiungere un altro metodo:

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Lasciami espandere solo per chiarire. Il codice seguente si traduce in ambiguità:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Quindi questo codice riportato sotto si traduce anche in ambiguità:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Tuttavia, questo non provoca ambiguità:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

2
Il compilatore può mappare la tua chiamata printSum (1, 2); a printSum (long a, long b) o printSum (int a, double b) quindi è ambiguo. Devi aiutare esplicitamente il compilatore a scegliere tra dando il tipo esattamente in questo modo: printSum (1, 2d)
Ravindra Ranwala

6
Hai citato erroneamente il messaggio di errore e la differenza è molto importante. Il vero messaggio di errore è: Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match- non è il metodo ambiguo, è la chiamata al metodo che è ambiguo.
Erwin Bolwidt,

1
@ErwinBolwidt il messaggio di errore era di eclissi scusa ho citato male. comunque, ancora non lo capisco perché l'aggiunta di printSum (int a, long b) rimuove il messaggio di errore.
Riruzen,

2
JLS-5.3 => Se il tipo dell'espressione non può essere convertito nel tipo del parametro mediante una conversione consentita in un contesto di invocazione lento, si verifica un errore di compilazione. sembra essere applicabile al contesto, ma non è molto semplice dedurre come. +1
Naman,

1
Abbiamo sicuramente bisogno di una questione canonica per questo: stackoverflow.com/... ".
Marco13

Risposte:


17

Penso che questo abbia qualcosa a che fare con la regola specifica di JLS del 15.12.2.5. Scelta del metodo più specifico . Si afferma che:

Se più di un metodo membro è sia accessibile sia applicabile all'invocazione di un metodo, è necessario sceglierne uno per fornire il descrittore per l'invio del metodo di runtime. Il linguaggio di programmazione Java utilizza la regola che viene scelto il metodo più specifico.

Come Java sceglie il metodo più specifico è ulteriormente spiegato dal testo:

L'intuizione informale è che un metodo è più specifico di un altro se qualsiasi invocazione gestita dal primo metodo può essere passata all'altro senza un errore di compilazione. In casi come un argomento di espressione lambda tipicamente esplicito (§15.27.1) o una chiamata a arità variabile (§15.12.2.4), è consentita una certa flessibilità per adattare una firma all'altra.

Nel caso del tuo esempio, tutti i metodi sono accessibili e applicabili all'invocazione del metodo, pertanto Java deve determinare quale di essi è più specifico .

Per questi metodi, nessuno può essere determinato per essere più specifico:

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

Il quarto metodo cancella l'ambiguità proprio perché soddisfa le condizioni necessarie per essere il più specifico .

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Cioè, (int, long) può essere passato a (int, double), (long, long) o (double, long) senza errori di compilazione.


2
Quindi, per rendere più estivo, se tutti i metodi sono accessibili dalla chiamata, allora java identifica il metodo, che può essere chiamato ad altri metodi senza errore di compilazione, se trovato lo usi come più specifico e chiamalo altrimenti errore di ambiguità? Penso che questa sia una risposta valida @riruzen.
Sandeep Kokate,

Grazie! Ho provato questo con (double, int) vs (double, long) e ha chiarito l'ambiguità, quindi (double, int) vs (long, double) è stato ambiguo come previsto.
Riruzen,

7

È davvero una domanda molto interessante. Esaminiamo passo passo le specifiche del linguaggio Java.

  1. Quando il compilatore sta cercando di identificare metodi potenzialmente applicabili, la prima cosa che fa è cercare i metodi applicabili da Strict Invocation .

  2. Nel tuo caso non esistono tali metodi, quindi il passaggio successivo è trovare i metodi applicabili da Loose Invocation

  3. A questo punto tutti i metodi corrispondono, quindi il metodo più specifico ( §15.12.2.5 ) viene scelto tra i metodi applicabili per invocazione libera.

Questo è un momento chiave, quindi diamo un'occhiata da vicino.

Un metodo applicabile m1 è più specifico di un altro metodo applicabile m2, per un'invocazione con espressioni di argomento e1, ..., ek, se si verifica una delle seguenti condizioni:

(Siamo interessati solo al seguente caso):

  • m2 non è generico e m1 e m2 sono applicabili mediante invocazione rigorosa o non consentita e dove m1 ha tipi di parametri formali S1, ..., Sn e m2 ha tipi di parametri formali T1, ..., Tn, il tipo Si è più specifico di Ti per l'argomento ei per tutti i (1 ≤ i ≤ n, n = k).

In poche parole, un metodo è più specifico se tutti i suoi tipi di parametri sono più specifici . E

Un tipo S è più specifico di un tipo T per qualsiasi espressione se S <: T ( §4.10 ).

Espressione S <: Tsignifica che Sè un sottotipo di T. Per i primitivi abbiamo la seguente relazione:

double > float > long > int

Quindi diamo un'occhiata ai tuoi metodi e vediamo quale è più specifico di altri.

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

In questo esempio il primo parametro del metodo 1 è ovviamente più specifico del primo parametro del metodo 2 (se li chiamate con valori interi:) printSum(1, 2). Ma il secondo parametro è più specifico per il metodo 2 , perché long < double. Quindi nessuno di questi metodi è più specifico di un altro. Ecco perché hai un'ambiguità qui.

Nel seguente esempio:

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

il primo tipo di parametro del metodo 1 è più specifico di quello nel metodo 2, poiché int < longe il secondo tipo di parametro è lo stesso per entrambi, ecco perché viene scelto il metodo 1.


Non ho sottovalutato la tua risposta, ma questa risposta non spiega perché (int, double) sia ambiguo con (long, long) poiché chiaramente, (int, double) dovrebbe essere più specifico rispetto al primo parametro.
Riruzen,

3
@riruzen spiega: doublenon è più specifico di long. E per scegliere il metodo, tutti i parametri di tipo devono essere più specifici: il tipo Si è più specifico di Ti per l'argomento ei per tutti i (1 ≤ i ≤ n, n = k)
Kirill Simonov,

Dovrebbe essere in aumento +1

0

perché il valore int può anche essere considerato doppio in java. significa che double a = 3è valido e lo stesso con il lungo long b = 3Quindi è per questo che sta creando un'ambiguità. Chiami

printSum(1, 2);

È confuso per tutti e tre i metodi, perché tutti e tre sono validi:

int a = 1;
double b =1;
long c = 1;

Puoi mettere la L alla fine, per specificare che è un valore lungo. per esempio:

printSum(1L, 2L);

per il doppio devi convertirlo:

printSum((double)1, 2L);

leggi anche il commento di @Erwin Bolwidt


Sì compilatore confuso per tutti e 3 i metodi, il primo compilatore cerca il tipo esatto e se non ne trova uno, prova a trovare il tipo compatibile, e in questo caso tutti sono compatibili.
Un altro programmatore il

2
Se ho 4 dichiarazioni di metodi anziché 3, l'ambiguità viene fuori: public static void printSum (int a, double b) public static void printSum (int a, long b) public static void printSum (long a, long b) public static void printSum (double a, long b) quindi, anche se sono d'accordo con la tua risposta, non risponde ancora alla domanda. Java fa la promozione automatica del tipo se il tipo esatto non è disponibile se non sbaglio. Non capisco perché diventi ambiguo con quei 3.
Riruzen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.