sábado, 15 de octubre de 2011

El enigma de los métodos sobrecargados y el error de ambigüedad



En el post anterior hice una explicación (no muy buena, lo sé) acerca de los métodos sobrecargados y como elige el compilador que método ejecutar (cuando varios métodos tienen lista de parámetros compatible con los argumentos de la llamada), En el libro de Katy Sierra se hace énfasis en que la mejor manera de aprender lo suficiente para pasar un examen de certificación de un lenguaje de programación es programando mucho, al terminar cada tema me siento frente a mi computadora y juego un poco con los tópicos del tema que leí, agotando mis dudas con el que lo sabe todo: el señor compilador, verifique la información del post anterior con él, pero me salto una duda ¿Qué pasa en un escenario como el siguiente?
public class Sobrecarga {

       void metodo(int n, byte b){
             System.out.println("int byte");
       }
       void metodo(short s, long l){
             System.out.println("short long");
       }
       public static void main(String[] args){
             Sobrecarga sc = new Sobrecarga();
             byte b = 1;
             sc.metodo(b, b);
       }
      
}
¿Qué se imprime en pantalla al ejecutar esto?
NADA, error de compilación, ojo el error no está en la declaración de los métodos, sino con los argumentos de llamada (byte,byte), hablándolo con un compañero de clase llegamos a una conclusión (basada solo en estar jugando con las declaraciones de los parámetros), la cual es: "el compilador se decide primero por el (short long)  en vez de (int byte) pues el primer parámetro es más pequeño  (short < int) pero con el segundo se topa una sorpresa (long >> byte), lo que no le agrada mucho". El punto es que al compilador le debe quedar claro el ”ganador” sin necesidad de algo monstruoso como un backtracking o algo similar.
Esto del error por ambigüedad no es exclusivo de los primitivos, también se puede dar en objetos aunque allí la situación está un poco más clara. Y se solo cuando el argumento de llamada es null.

public class  VarArgs{
       public void metodo(Object o){
             System.out.println("Object");
       }
       public void metodo(String s){
             System.out.println("String");
       }
      
       public void metodo(int... n){
             System.out.println("int...n");
       }
       public  static void main(String [] args){
             VarArgs va = new VarArgs();
             va.metodo(null);
       }
}
null es válido para las tres versiones sobrecargadas del método, aquí aparece un error de compilación.
Para responder ¿Por qué? lo hare con otro ejemplo.
class A{ }
class B extends A{ }

public class  VarArgs{
       public void metodo(Object o){
             System.out.println("Object");
       }
       public void metodo(A a){
             System.out.println("A");
       }
      
       public void metodo(B b){
             System.out.println("B");
       }
       public  static void main(String [] args){
             VarArgs va = new VarArgs();
             va.metodo(null);
       }
}
Aquí no existe ningún error de compilación y se imprime en pantalla
B
Cuando el compilador tiene una llamada a un método (con parámetros con referencias a objetos) en la cual varios métodos pueden responder a ella se ejecutara el que este más debajo de la jerarquía de herencia de clases. Aquí está muy clara
Object
A
B
 Pero en el ejemplo anterior no lo estaba
Object
String        ¿?  int[]
Ese era el motivo precisamente, y por esto solo se da en el caso de null pues es un valor valido para cualquier referencia a objeto.

1 comentario:

  1. aha, esto tiene que ver con como el compilador trata la firma de los metodos, creo que le llaman boxing y unboxing, tambien me recuerda un poco a los retornos covariantes, lo dificil de esto quizas es saber el arbol genealogico de cada clase, luego es bastante criterioso como el compilador trabaja con las sobrecargas

    ResponderEliminar