Pregunta

Me di cuenta hoy de que el boxeo automático a veces puede causar ambigüedad en la resolución de sobrecarga del método. El ejemplo más simple parece ser este:

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); }
}

Cuando se compila, causa el siguiente error:

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 solución a este error es trivial: solo use el auto-boxing explícito:

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

Que llama correctamente a la primera sobrecarga como se esperaba.

Entonces, ¿por qué falló la resolución de sobrecarga? ¿Por qué el compilador no recuperó automáticamente el primer argumento y aceptó el segundo argumento normalmente? ¿Por qué tuve que solicitar el auto-boxeo explícitamente?

¿Fue útil?

Solución

Cuando lanza el primer argumento a Object usted mismo, el compilador coincidirá con el método sin usar autoboxing (JLS3 15.12.2):

  

La primera fase (& # 167; 15.12.2.2) realiza   resolución de sobrecarga sin permitir   conversión de boxeo o unboxing, o la   uso del método de aridad variable   invocación. Si no hay un método aplicable es   encontrado durante esta fase entonces   el procesamiento continúa hasta el segundo   fase.

Si no lo expulsa explícitamente, pasará a la segunda fase de intentar encontrar un método de coincidencia, permitiendo el autoboxing, y luego es realmente ambiguo, porque su segundo argumento puede coincidir con booleano u Objeto.

  

La segunda fase (& # 167; 15.12.2.3) realiza   resolución de sobrecarga mientras permite   boxeo y unboxing, pero aun así   impide el uso de aridad variable   invocación de método.

¿Por qué, en la segunda fase, el compilador no elige el segundo método porque no es necesario el autoboxing del argumento booleano? Debido a que después de haber encontrado los dos métodos de coincidencia, solo se utiliza la conversión de subtipo para determinar el método más específico de los dos, independientemente de cualquier boxeo o unboxing que se haya realizado para unirlos en primer lugar (& # 167; 15.12. 2.5).

Además: el compilador no siempre puede elegir el método más específico en función del número de auto (des) boxeo necesario. Todavía puede dar lugar a casos ambiguos. Por ejemplo, esto sigue siendo 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
}

Recuerde que el algoritmo para elegir un método de coincidencia (paso 2 del tiempo de compilación) es fijo y se describe en el JLS. Una vez en la fase 2 no hay autoboxing o unboxing selectivo. El compilador localizará todos los métodos que son accesibles (ambos métodos en estos casos) y aplicables (nuevamente los dos métodos), y solo entonces elige el más específico sin mirar boxing / unboxing, que es ambiguo aquí.

Otros consejos

El compilador hizo auto-box el primer argumento. Una vez hecho esto, es el segundo argumento que es ambiguo, ya que podría verse como booleano u Objeto.

Esta página explica las reglas para autoboxing y seleccionar qué método invocar. El compilador primero intenta seleccionar un método sin usar ningún autoboxing , porque el boxeo y el unboxing conllevan penalizaciones de rendimiento. Si no se puede seleccionar ningún método sin recurrir al boxeo, como en este caso, entonces el boxeo está en la tabla para todos argumentos para ese método.

Cuando dices f (a, b ), el compilador no sabe a qué función debe hacer referencia.

Esto se debe a que a es un int , pero el argumento esperado en f es un Objeto. Entonces el compilador decide convertir a en un Objeto. Ahora el problema es que, si a puede convertirse en un objeto, también puede ser b .

Esto significa que la llamada a la función puede hacer referencia a cualquiera de las definiciones. Esto hace que la llamada sea ambigua.

Cuando convierte a en un Objeto manualmente, el compilador solo busca la coincidencia más cercana y luego se refiere a ella.

  

¿Por qué el compilador no seleccionó el   función que se puede alcanzar con " haciendo   el menor número posible de   conversiones de boxeo / unboxing " ;?

Vea el siguiente caso:

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

Si llamamos como f (boolean a, boolean b) , ¿qué función debería seleccionar? Es ambiguo ¿verdad? Del mismo modo, esto se volverá más complejo cuando haya muchos argumentos presentes. Entonces el compilador eligió darte una advertencia en su lugar.

Dado que no hay forma de saber a cuál de las funciones realmente pretendía llamar el programador, el compilador da un error.

  

Entonces, ¿por qué la resolución de sobrecarga   ¿fallar? ¿Por qué el compilador no auto-box   el primer argumento, y acepte el   segundo argumento normalmente? Por qué yo   tener que solicitar auto-boxeo   explícitamente?

No aceptó el segundo argumento normalmente. Recuerde que & Quot; boolean & Quot; también se puede encuadrar en un Objeto. También podría haber lanzado explícitamente el argumento booleano a Object y hubiera funcionado.

Consulte http: //java.sun. com / docs / books / jls / third_edition / html / expressions.html # 20448

El reparto ayuda porque entonces no se necesita boxeo para encontrar el método para llamar. Sin el reparto, el segundo intento es permitir el boxeo y luego también el booleano se puede boxear.

Es mejor tener especificaciones claras y comprensibles para decir lo que sucederá que hacer que la gente adivine.

El compilador de Java resuelve métodos y constructores sobrecargados en fases. En la primera fase [& # 167; 15.12.2.2], identifica los métodos aplicables subtipando [& # 167; 4.10]. En este ejemplo, ninguno de los métodos es aplicable, porque int no es un subtipo de Object.

En la segunda fase [& # 167; 15.12.2.3], el compilador identifica los métodos aplicables mediante la conversión de invocación de método [& # 167; 5.3], que es una combinación de autoboxing y subtipo. El argumento int se puede convertir a un entero, que es un subtipo de objeto, para ambas sobrecargas. El argumento booleano no necesita conversión para la primera sobrecarga, y se puede convertir a booleano, un subtipo de objeto, para la segunda. Por lo tanto, ambos métodos son aplicables en la segunda fase.

Dado que se aplica más de un método, el compilador debe determinar cuál es el más específico [& # 167; 15.12.2.5]. Compara los tipos de parámetros, no los tipos de argumentos, y no los autobox. Objeto y booleano son tipos no relacionados, por lo que se consideran igualmente específicos. Ninguno de los métodos es más específico que el otro, por lo que la llamada al método es ambigua.

Una forma de resolver la ambigüedad sería cambiar el parámetro booleano para escribir Boolean, que es un subtipo de Object. La primera sobrecarga siempre sería más específica (cuando corresponda) que la segunda.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top