tipos sobrecarga que polimorfismo métodos metodos herencia ejercicios constructores java compiler-errors ambiguous-call

que - Java: selección entre constructores sobrecargados



sobrecarga de metodos en java (3)

Este comportamiento se debe a que E no es más específico que A, ya que pertenecen a jerarquías diferentes, no se pueden comparar. Por lo tanto, cuando pasa un nulo, Java no puede saber qué jerarquía usar.

Según esta pregunta , Java seleccionará la opción "más específica" cuando intente seleccionar entre constructores sobrecargados ambiguos. En este ejemplo:

public class Test{ private Test(Map map){ System.out.println("Map"); } private Test(Object o){ System.out.println("Object"); } public static void main(String[] args){ new Test(null); } }

se imprimirá

"Mapa"

Sin embargo, estaba tratando de averiguar exactamente qué significa "más específico". Supuse que significaba "menos ambiguo", como en "puede referirse al menor número posible de tipos". En este contexto, Object puede ser cualquier cosa que no sea primitiva, mientras que Map puede ser solo Map o ? extends Map ? extends Map . Básicamente, asumí que cualquier clase que estuviera más cerca de la hoja del árbol de herencia sería seleccionada. Eso funciona cuando una clase es una subclase de la otra:

public class Test{ private Test(A a){ System.out.println("A"); } private Test(B b){ System.out.println("B"); } public static void main(String[] args){ new Test(null); } } class A{} class B extends A{}

"SEGUNDO"

Entonces se me ocurrió esto:

public class Test{ private Test(A a){ System.out.println("A"); } private Test(E e){ System.out.println("E"); } public static void main(String[] args){ new Test(null); } } class A{} class B extends A{} class C{} class D extends C{} class E extends D{}

Creo que debería imprimir E , ya que E solo puede referirse a un tipo conocido, mientras que A puede referirse a dos ( A y B ). Pero da un error de referencia ambiguo.

¿Cómo es realmente elegir el constructor? Leí los documentos pero, francamente, no pude entender cómo determina la especificidad. Espero una descripción de por qué no puede determinar que E es más específico que A


La siguiente declaración de 15.12.2.5 responde que,

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 podría pasarse a la otra sin un error en tiempo de compilación.

Caso 1

  • Puede pasar cualquier cosa en el constructor que toma Object mientras que no podemos pasar nada que no sea un Map en el primer constructor. Así que todo lo que paso en el constructor del Map puede ser manejado por el constructor del Object y es por eso que el Test(Map map) vuelve mote específico.

Caso 2

  • Como B extiende A , aquí Test(B b) constructor de Test(B b) vuelve más específico. Como podemos pasar B en Test(A a) gracias a la herencia .

Caso 3

  • En este caso, no hay una conversión directa para representar el método más específico y resulta en ambigüedad .

No se basa en la cantidad de tipos que son convertibles en el tipo de parámetro, sino en que cualquier valor válido para una sobrecarga sea válido para otro debido a conversiones implícitas.

Por ejemplo, hay una conversión implícita de String a Object , pero el reverso no es verdadero, por lo que String es más específico que Object .

Del mismo modo, hay una conversión implícita de B a A , pero el reverso no es verdadero, por lo que B es más específico que A

Sin embargo, con A y E , ninguno es más específico que el otro, no hay conversión de A a E ni conversión de E a A Es por eso que la resolución de sobrecarga falla.

El bit relevante del JLS es en realidad 15.12.2.5 , que incluye esto que podría facilitar su comprensión:

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 podría pasarse a la otra sin un error en tiempo de compilación.

Entonces si tienes:

void foo(String x) void foo(Object x)

cada invocación manejada por foo(String) podría ser manejada por foo(Object) , pero lo contrario no es el caso. (Por ejemplo, podría llamar a foo(new Object()) y eso no podría ser manejado por foo(String) .)