promoción de tipo java en parámetros
java-8 type-promotion (3)
Creo que esto tiene algo que ver con la regla específica de JLS sobre 15.12.2.5. Elegir el método más específico . Se afirma que:
Si más de un método miembro es accesible y aplicable a una invocación de método, es necesario elegir uno para proporcionar el descriptor para el envío del método en tiempo de ejecución. El lenguaje de programación Java usa la regla de que se elige el método más específico.
El texto explica cómo Java elige el método más específico :
La intuición informal es que un método es más específico que otro si cualquier invocación manejada por el primer método pudiera pasar al otro sin un error en tiempo de compilación. En casos como un argumento de expresión lambda tipado explícitamente (§15.27.1) o una invocación de aridad variable (§15.12.2.4), se permite cierta flexibilidad para adaptar una firma a la otra.
En el caso de su ejemplo, todos los métodos son accesibles y aplicables a la invocación de métodos, por lo tanto, Java necesita determinar cuál de ellos es más específico .
Para estos métodos, no se puede determinar que sea más específico:
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
El cuarto método borra la ambigüedad precisamente porque cumple la condición necesaria para ser más específico .
public static void printSum(int a, long b) {
System.out.println(String.format("%s, %s ", a, b));
}
Es decir, (int, long) puede pasarse a (int, double), (long, long) o (double, long) sin errores de compilación.
Me topé con este fragmento:
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);
}
}
Esto dará como resultado un error de compilación:
Error: (15, 9) java: la referencia a printSum es ambigua tanto en el método printSum (int, double) en ParamTest como en el método printSum (long, long) en ParamTest match
¿Cómo es esto ambiguo? ¿No debería promoverse solo el segundo parámetro en este caso ya que el primer parámetro ya es un int? El primer parámetro no necesita ser promovido en este caso, ¿verdad?
La compilación tiene éxito si actualizo el código para agregar otro método:
public static void printSum(int a, long b) {
System.out.println(String.format("%s, %s ", a, b));
}
Déjame expandirme solo para aclarar. El siguiente código genera ambigüedad:
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);
}
}
Entonces este código a continuación también genera ambigüedad:
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);
}
}
Sin embargo, este no da lugar a ambigüedad:
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);
}
}
De hecho, es una pregunta muy interesante. Veamos paso a paso la especificación del lenguaje Java.
-
Cuando el compilador intenta identificar métodos potencialmente aplicables, lo primero que hace es buscar métodos aplicables por invocación estricta .
-
En su caso, no existen tales métodos, por lo que el siguiente paso es encontrar los métodos aplicables por Invocación suelta
-
En este punto, todos los métodos coinciden, por lo que el método más específico ( §15.12.2.5 ) se elige entre los métodos que son aplicables por invocación flexible.
Este es un momento clave, así que veamos esto de cerca.
Un método aplicable m1 es más específico que otro método aplicable m2, para una invocación con expresiones de argumento e1, ..., ek, si alguno de los siguientes es verdadero:
(Estamos interesados solo en el siguiente caso):
- m2 no es genérico, y m1 y m2 son aplicables por invocación estricta o flexible, y donde m1 tiene tipos de parámetros formales S1, ..., Sn y m2 tiene tipos de parámetros formales T1, ..., Tn, el tipo Si es más específico que Ti para el argumento ei para todo i (1 ≤ i ≤ n, n = k).
En pocas palabras, un método es más específico si todos sus tipos de parámetros son más específicos . Y
Un tipo S es más específico que un tipo T para cualquier expresión si S <: T ( §4.10 ).
La expresión
S <: T
significa que
S
es un subtipo de
T
Para los primitivos tenemos la siguiente relación:
double > float > long > int
Así que echemos un vistazo a sus métodos y veamos cuál es más específico que otros.
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));
}
En este ejemplo, el primer parámetro del método 1 es obviamente más específico que el primer parámetro del método 2 (si los llama con valores enteros:
printSum(1, 2)
).
Pero el segundo parámetro es más específico para el método 2
, porque
long < double
.
Por lo tanto, ninguno de estos métodos es más específico que otro.
Es por eso que tienes una ambigüedad aquí.
En el siguiente ejemplo:
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));
}
el primer tipo de parámetro del método 1 es más específico que el del método 2, porque
int < long
y el segundo tipo de parámetro es el mismo para ambos, por eso se elige el método 1.
porque el valor int también se puede considerar como doble en java.
significa que
double a = 3
es válido y lo mismo con el largo
long b = 3
Así que por eso está creando una ambigüedad.
Llama
printSum(1, 2);
Es confuso para los tres métodos, porque todos estos tres son válidos:
int a = 1;
double b =1;
long c = 1;
Puede poner la L al final, para especificar que es un valor largo. por ejemplo:
printSum(1L, 2L);
para el doble necesitas convertirlo:
printSum((double)1, 2L);
También lea el comentario de @Erwin Bolwidt