varargs reciben que parametros metodos indefinidos con argumentos java method-overloading overloading

java - reciben - ¿Por qué el compilador prefiere una sobrecarga int a una sobrecarga varargs char para un char?



metodos que reciben parametros en java (4)

Código

public class TestOverload { public TestOverload(int i){System.out.println("Int");} public TestOverload(char... c){System.out.println("char");} public static void main(String[] args) { new TestOverload(''a''); new TestOverload(65); } }

Salida

Int Int

¿Es el comportamiento esperado? Si es así, ¿por qué? Estoy esperando: char, Int

Nota: estoy usando Java 8


Consejo sólido de Joshua Bloch (Effective Java, 2nd Ed):

"solo elige como argumentos para un método sobrecargado aquellos que tienen tipos radicalmente diferentes".

Un objeto con un tipo radicalmente diferente es aquel que no se puede convertir razonablemente en otro de los tipos de argumento. Seguir esta regla puede ahorrarle horas de depuración de un misterioso error que puede ocurrir cuando el compilador elige en tiempo de compilación la sobrecarga del método que no esperaba.

Sus líneas de código violan esta regla y abren la puerta a errores:

public TestOverload(int i){System.out.println("Int");} public TestOverload(char... c){System.out.println("char");}

Un char es interconvertible con un int y, por lo tanto, la única forma de predecir lo que sucederá con las invocaciones es ir a la Especificación del lenguaje Java y leer las reglas algo arcanas sobre cómo se resuelven las sobrecargas.

Afortunadamente, esta situación no debería necesitar investigación de JLS. Si tiene argumentos que no son radicalmente diferentes entre sí, probablemente la mejor opción es no sobrecargar . Dé a los métodos diferentes nombres para que no haya posibilidad de error o confusión por parte de cualquiera que necesite mantener el código.

El tiempo es dinero.


Los métodos con varargs ( ... ) tienen la prioridad más baja cuando el compilador determina qué método sobrecargado elegir. Por TestOverload(int i) tanto, TestOverload(int i) se elige sobre TestOverload(char... c) cuando llama a TestOverload con un solo parámetro char ''a'' , ya que un char puede promocionarse automáticamente a int .

JLS 15.12.2 :

  1. La primera fase (§15.12.2.2) realiza la resolución de sobrecarga sin permitir la conversión de boxeo o unboxing, o el uso de la invocación del método de aridad variable . Si no se encuentra ningún método aplicable durante esta fase, el procesamiento continúa a la segunda fase. Esto garantiza que las llamadas que fueron válidas en el lenguaje de programación Java antes de Java SE 5.0 no se consideran ambiguas como resultado de la introducción de métodos de aridad variable, boxeo implícito y / o unboxing. Sin embargo, la declaración de un método de arity variable (§8.4.1) puede cambiar el método elegido para una expresión de invocación de método de método dado, porque un método de arity variable se trata como un método de arity fijo en la primera fase. Por ejemplo, declarar m (Objeto ...) en una clase que ya declara m (Objeto) hace que m (Objeto) ya no se elija para algunas expresiones de invocación (como m (nulo)), como m (Objeto [] ) es más específico.

  2. La segunda fase (§15.12.2.3) realiza la resolución de sobrecarga al tiempo que permite el boxeo y el desempaquetado, pero aún impide el uso de la invocación del método de aridad variable . Si no se encuentra ningún método aplicable durante esta fase, el procesamiento continúa a la tercera fase. Esto garantiza que nunca se elija un método mediante la invocación del método de aridad variable si es aplicable mediante la invocación del método de aridad fija.

  3. La tercera fase (§15.12.2.4) permite que la sobrecarga se combine con métodos de arity variables , boxing y unboxing.

EDITAR:

Si desea forzar al compilador a llamar al TestOverload(char... c) , puede pasar al constructor llamar a un char[] :

new TestOverload (new char[] {''a''});


Sí, es comportamiento esperado. La precedencia para la llamada al método es la siguiente:

  1. Ampliando
  2. Boxeo
  3. Varargs

A continuación se muestra un extracto de documentos de Java relacionados con el mismo:

El proceso de determinar la aplicabilidad comienza por determinar los métodos potencialmente aplicables (§15.12.2.1).

El resto del proceso se divide en tres fases, para garantizar la compatibilidad con las versiones del lenguaje de programación Java anteriores a Java SE 5.0. Las fases son:

La primera fase (§15.12.2.2) realiza la resolución de sobrecarga sin permitir la conversión de boxeo o unboxing, o el uso de la invocación del método de aridad variable. Si no se encuentra ningún método aplicable durante esta fase, el procesamiento continúa a la segunda fase.

Esto garantiza que las llamadas que fueron válidas en el lenguaje de programación Java antes de Java SE 5.0 no se consideran ambiguas como resultado de la introducción de métodos de aridad variable, boxeo implícito y / o unboxing. Sin embargo, la declaración de un método de arity variable (§8.4.1) puede cambiar el método elegido para una expresión de invocación de método de método dado, porque un método de arity variable se trata como un método de arity fijo en la primera fase. Por ejemplo, declarar m (Objeto ...) en una clase que ya declara m (Objeto) hace que m (Objeto) ya no se elija para algunas expresiones de invocación (como m (nulo)), como m (Objeto [] ) es más específico.

La segunda fase (§15.12.2.3) realiza la resolución de sobrecarga al tiempo que permite el boxeo y el desempaquetado, pero aún impide el uso de la invocación del método de aridad variable. Si no se encuentra ningún método aplicable durante esta fase, el procesamiento continúa a la tercera fase.

Esto garantiza que nunca se elija un método mediante la invocación del método de aridad variable si es aplicable mediante la invocación del método de aridad fija.

La tercera fase (§15.12.2.4) permite que la sobrecarga se combine con métodos de arity variables, boxing y unboxing.


Tomé el código de este enlace y modifiqué algunas partes:

public static void main(String[] args) { Byte i = 5; byte k = 5; aMethod(i, k); } //method 1 static void aMethod(byte i, Byte k) { System.out.println("Inside 1"); } //method 2 static void aMethod(byte i, int k) { System.out.println("Inside 2"); } //method 3 static void aMethod(Byte i, Byte k) { System.out.println("Inside 3 "); } //method 4 static void aMethod(Byte i, Byte ... k) { System.out.println("Inside 4 "); }

El compilador da error (El método es ambiguo para el tipo Sobrecarga) para los métodos 1, 2 y 3 pero no 4 (¿por qué?)

La respuesta se encuentra en el mecanismo que utiliza Java para hacer coincidir las llamadas a métodos con las firmas de métodos. El mecanismo se realiza en tres fases, en cada fase si encuentra un método coincidente se detiene:

+ fase uno: use la ampliación para encontrar el método de coincidencia (no se encontraron métodos de coincidencia)

+ fase dos: (también) use boxing / unboxing para encontrar el método de coincidencia (método 1,2 y 3 coincidencia)

+ fase tres: (también) usa var args (¡método 4 coincidencias!)