Varargs Java Ambiguous Call

Sono un po ‘confuso sui metodi varargs di Java:

public static int sum(int ...a) { return 0; } public static double sum(double ...a) { return 0.0; } 

Quando provavo a invocare sum() senza passare alcun argomento, veniva invocata la versione int del metodo. Non capisco perché; normalmente il compilatore deve generare un errore.

Al contrario, la seguente parte di codice genera un errore del compilatore quando provo a richiamare la sum senza alcun argomento:

 public static int sum(int ...a) { return 0; } public static boolean sum(boolean ...a) { return true; } 

La regola generale che si applica qui è questa: se una firma del metodo è strettamente più specifica dell’altra, allora Java la sceglie senza errori.

Intuizionalmente, la firma di un metodo è più specifica se è ansible eliminarla interamente e l’altra, meno specifica, sarebbe applicabile a ciascuna chiamata esistente.

Quando viene presentata una scelta tra la sum(int... args) firme sum(int... args) e sum(double... args) , la sum(int... args) della firma sum(int... args) è più specifica perché qualsiasi invocazione di quel metodo potrebbe anche essere passata per sum(double... args) applicando una conversione allargata. Lo stesso non vale per un metodo sum(boolean... args) , che non può essere convertito in modo simile.

Java Language Specification, versione SE 8:

15.12. Metodo Invocation Expressions

15.12.2.5. Scelta del metodo più specifico

Il linguaggio di programmazione Java utilizza la regola per scegliere il metodo più specifico .

Un metodo applicabile m1 è più specifico di un altro metodo applicabile m2, per un’invocazione con espressioni di argomento e1, …, ek, se una delle seguenti condizioni è vera:

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

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


4.10. subtyping

4.10.1. Sottotitoli tra tipi primitivi

doppio> 1 galleggiante

fluttua> 1 lungo

lungo> 1 int

Come menzionato in questa risposta , ci sono delle regole seguite quando si seleziona quale metodo di overload usare.

Per citare:

  1. L’allargamento primitivo utilizza l’argomento del metodo più piccolo ansible
  2. Il tipo di wrapper non può essere ampliato con un altro tipo di wrapper
  3. Puoi box da int a Integer e allargare a Object ma no a Long
  4. Battiti allargati Boxe, Boxe batte Var-args.
  5. Puoi Box e quindi Widen (Un int può diventare Object via intero)
  6. Non puoi Widen e quindi Box (Un int non può diventare Long)
  7. Non è ansible combinare var-args, sia con l’ampliamento che con il pugilato.

(Ridefiniamo la regola 1 in questo modo: “L’ampliamento primitivo utilizza l’argomento del metodo più specifico ansible.”)

Quindi, con queste regole in mente, possiamo avere un’idea di cosa sta succedendo qui:

Secondo la regola numero uno, l’allargamento primitivo utilizza l’argomento del metodo più specifico ansible. Poiché un int è rappresentato da un numero non decimale (es. 1 ) e un double è rappresentato da un numero decimale con precisione 32 byte più di quello di un float (es. 1.0 ), possiamo dire che int s sono “meno di “o” minore di ” double s, e con questa logica, int s può essere” promosso “a double s e double s può essere” declassato “a int s.

In parole povere, una primitiva che può essere allargata ad un’altra primitiva (es. int -> float -> double ) è più specifica di un’altra. Ad esempio, un int è più specifico di un double perché 1 può essere promosso a 1.0 .

Quando non si passavano argomenti a questi metodi vararg sovraccaricati con lo stesso nome, poiché il ritorno è effettivamente lo stesso (rispettivamente 0 e 0,0), il compilatore sceglierebbe di usare il metodo che accetta un vararg di tipo int poiché è più specifico .

Quindi, quando hai introdotto questi stessi metodi che contengono rispettivamente int s e boolean (i tipi che non possono essere ampliati l’uno con l’altro), il compilatore ora non può scegliere un metodo da usare poiché int s non può essere “promosso” o “retrocesso” “come int s, float s e double s. Pertanto, genererà un errore di compilazione.

Spero che questo ti aiuti a capire cosa sta succedendo.