Un nombre, varias versiones
En la página anterior viste cómo los parámetros de un método definen lo que acepta. La sobrecarga de métodos va más allá: puedes dar a varios métodos el mismo nombre siempre que sus listas de parámetros difieran. El compilador los trata como métodos distintos y elige el correcto según los argumentos que pasas.
Por eso System.out.println imprime sin problema un int, un String, un boolean o un double: no existe un único println, sino muchas sobrecargas que comparten el nombre. Tú escribes la llamada que quieres y el compilador la asocia a la versión que encaja.
Ambos métodos se llaman square, pero uno recibe un int y el otro un double. El literal 5 es un int, así que se ejecuta la primera sobrecarga; 2.5 es un double, así que se ejecuta la segunda.
Qué cuenta como una sobrecarga distinta
Las sobrecargas deben diferir en su lista de parámetros, es decir, al menos en uno de estos aspectos:
- un número distinto de parámetros,
- tipos de parámetros distintos, o
- un orden distinto de los tipos.
Cada llamada tiene una lista de parámetros que coincide exactamente con uno de los tres métodos join, así que no hay confusión.
El tipo de retorno no cuenta
Una trampa habitual para principiantes: intentar sobrecargar solo por el tipo de retorno. El tipo de retorno no forma parte de la firma que usa el compilador, así que esto no compila:
// NO compila - mismo nombre, mismos parámetros, solo cambia el tipo de retorno
static int value() { return 1; }
static double value() { return 1.0; } // error: value() ya está definido
El compilador no puede distinguirlos, porque en un punto de llamada como value() nada en los argumentos indica cuál quieres. Puedes dar a las sobrecargas tipos de retorno distintos, pero solo cuando sus listas de parámetros ya difieren.
Cómo elige Java una sobrecarga
Cuando más de una sobrecarga podría aceptar tus argumentos, Java elige la más específica y prefiere una coincidencia de tipo exacta antes que una conversión de ampliación. Mira lo que ocurre con un argumento int:
show(7) coincide exactamente con int aunque long y double también podrían contener un 7 tras una ampliación. Solo si se eliminara la sobrecarga exacta el compilador ampliaría int a long y luego a double. Esta resolución se decide por completo en tiempo de compilación, según los tipos declarados de tus argumentos.
Cuidado con las llamadas ambiguas
Si ninguna sobrecarga es claramente la mejor coincidencia, el compilador se niega a adivinar y reporta un error. Esto ocurre con más frecuencia con null, que encaja en cualquier tipo de referencia:
static void handle(String s) { }
static void handle(StringBuilder b) { }
handle(null); // error: la referencia a handle es ambigua
Ambas sobrecargas aceptan null y ninguna es más específica, así que la llamada no compila. Arréglalo haciendo el tipo explícito con un cast - handle((String) null) - o rediseñando para que las sobrecargas no choquen. El mismo cuidado aplica al mezclar autoboxing y ampliación; mantén los conjuntos de sobrecargas lo bastante simples como para que cada llamada tenga un ganador evidente.
Sobrecarga de constructores
La sobrecarga no se limita a los métodos ordinarios: los constructores la usan constantemente para ofrecer varias formas de construir un objeto. Un constructor sin argumentos puede delegar en uno más completo con this(...):
Los dos constructores comparten el nombre Point pero difieren en el número de parámetros, exactamente como los métodos sobrecargados. Delegar con this(...) mantiene la lógica de inicialización en un solo lugar.
Sobrecarga frente a sobrescritura
Estos dos términos suenan parecidos, pero no están relacionados:
- Sobrecarga - mismo nombre, listas de parámetros distintas, en la misma clase. El compilador elige la versión en tiempo de compilación. Se trata de ofrecer variantes de una operación.
- Sobrescritura - una subclase redefine un método heredado con el mismo nombre y los mismos parámetros. Java elige la versión en tiempo de ejecución según el tipo real del objeto. Se trata de reemplazar comportamiento (lo verás con la herencia y el polimorfismo).
Si las listas de parámetros son idénticas, estás sobrescribiendo (o provocando un error de método duplicado en la misma clase); si difieren, estás sobrecargando.
Siguiente: Varargs
La sobrecarga te permite escribir join(a, b) y join(a, b, c) como métodos separados, pero ¿y si quieres aceptar cualquier número de argumentos sin declarar una sobrecarga para cada cantidad? La sintaxis de varargs de Java permite que un solo método reciba una lista de argumentos de longitud variable, y ese es el tema de la próxima página.
Preguntas frecuentes
¿Qué es la sobrecarga de métodos en Java?
La sobrecarga de métodos consiste en definir varios métodos con el mismo nombre dentro de la misma clase, cada uno con una lista de parámetros diferente (distinto número de parámetros, distintos tipos o distinto orden de tipos). El compilador decide qué versión llamar comparando los argumentos que pasas con los parámetros de cada sobrecarga. Es una decisión que se toma en tiempo de compilación, no en tiempo de ejecución.
¿Pueden dos métodos diferenciarse solo por el tipo de retorno en Java?
No. El tipo de retorno no forma parte de la firma de un método a efectos de la sobrecarga, así que int total() y double total() en la misma clase provocan un error de compilación. Las sobrecargas deben diferir en su lista de parámetros: el número, los tipos o el orden de los parámetros. El tipo de retorno puede ser distinto, pero solo además de una diferencia en los parámetros, no por sí solo.
¿Cuál es la diferencia entre sobrecarga y sobrescritura en Java?
La sobrecarga son varios métodos con el mismo nombre pero parámetros distintos en la misma clase, resueltos por el compilador en tiempo de compilación. La sobrescritura es una subclase que redefine un método heredado con el mismo nombre y los mismos parámetros, resuelto en tiempo de ejecución según el tipo real del objeto. La sobrecarga consiste en ofrecer variantes; la sobrescritura, en reemplazar comportamiento.