Java Challengers # 1: sobrecarga de métodos en la JVM
Buen dia a todos.
Ya hemos lanzado el siguiente hilo del curso "Desarrollador Java" , pero todavía tenemos algunos materiales que nos gustaría compartir con ustedes.
¡Bienvenido a la serie de artículos Java Challengers ! Esta serie de artículos se centra en las características de programación de Java. Su desarrollo es su manera de convertirse en un programador Java altamente calificado.
Dominar las técnicas discutidas en esta serie de artículos requiere un poco de esfuerzo, pero su experiencia diaria como desarrollador de Java será de gran utilidad. Evitar errores es más fácil cuando sabes cómo aplicar correctamente las técnicas básicas de programación Java y rastrear errores es mucho más fácil cuando sabes exactamente lo que está sucediendo en tu código Java.
¿Estás listo para comenzar a dominar los conceptos básicos de programación en Java? ¡Entonces comencemos con nuestro primer rompecabezas!

El término "sobrecarga de método"
Sobre el término sobrecarga, los desarrolladores tienden a pensar que estamos hablando de reiniciar el sistema, pero esto no es así. En programación, la sobrecarga de métodos significa usar el mismo nombre de método con diferentes parámetros.
¿Qué es la sobrecarga de métodos?
La sobrecarga de métodos es una técnica de programación que permite a un desarrollador de la misma clase usar el mismo nombre para métodos con diferentes parámetros. En este caso, decimos que el método está sobrecargado.
El Listado 1 muestra métodos con diferentes parámetros que varían en número, tipo y orden.
Listado 1. Tres opciones para métodos de sobrecarga.
Método de sobrecarga y tipos primitivos
En el Listado 1, vio los tipos primitivos int
y double
. Divaguemos por un minuto y recordemos los tipos primitivos en Java.
Tabla 1. Tipos primitivos en Java
Tipo | Alcance | Valor por defecto | Tamaño | Ejemplos literales |
---|
booleano | verdadero o falso | falso | 1 bit | verdadero falso |
byte | -128 ... 127 | 0 0 | 8 bit | 1, -90, -128 |
char | Carácter Unicode o 0 a 65 536 | \ u0000 | 16 bit | 'a', '\ u0031', '\ 201', '\ n', 4 |
corta | -32,768 ... 32,767 | 0 0 | 16 bit | 1, 3, 720, 22,000 |
int | -2 147 483 648 ... 2 147 483 647 | 0 0 | 32 bit | -2, -1, 0, 1, 9 |
largo | -9,223,372,036,854,775,808 a 9,223,372,036,854,775,807 | 0 0 | 64 bit | -4000L, -900L, 10L, 700L |
flotar | 3.40282347 x 1038, 1.40239846 x 10-45 | 0.0 | 32 bit | 1.67e200f, -1.57e-207f, .9f, 10.4F |
doble | 1.7976931348623157 x 10308, 4.9406564584124654 x 10-324 | 0.0 | 64 bit | 1.e700d, -123457e, 37e1d |
¿Por qué debería usar la sobrecarga de métodos?
El uso de sobrecarga hace que su código sea más limpio y fácil de leer, y también ayuda a evitar errores en el programa.
A diferencia del Listado 1, imagine un programa donde tendrá muchos métodos de calculate()
con nombres similares a calculate1
, calculate2
, calculate3
... no es bueno, ¿verdad? Sobrecargar el método calculate()
permite usar el mismo nombre y cambiar solo lo que se necesita: parámetros. También es muy fácil encontrar métodos sobrecargados, ya que están agrupados en código.
¿Qué sobrecarga no es
Recuerde que cambiar el nombre de una variable no es una sobrecarga. El siguiente código no se compila:
public class Calculator { void calculate(int firstNumber, int secondNumber){} void calculate(int secondNumber, int thirdNumber){} }
Tampoco puede sobrecargar el método cambiando el valor de retorno en la firma del método. Este código tampoco compila:
public class Calculator { double calculate(int number1, int number2){return 0.0;} long calculate(int number1, int number2){return 0;} }
Sobrecarga del constructor
Puede sobrecargar el constructor de la misma manera que el método:
public class Calculator { private int number1; private int number2; public Calculator(int number1) { this.number1 = number1; } public Calculator(int number1, int number2) { this.number1 = number1; this.number2 = number2; } }
Resolver el problema de sobrecarga de métodos
¿Estás listo para la primera prueba? ¡Averígualo!
Comience examinando cuidadosamente el siguiente código.
Listado 2. El desafío de la sobrecarga de métodos
public class AdvancedOverloadingChallenge3 { static String x = ""; public static void main(String... doYourBest) { executeAction(1); executeAction(1.0); executeAction(Double.valueOf("5")); executeAction(1L); System.out.println(x); } static void executeAction(int ... var) {x += "a"; } static void executeAction(Integer var) {x += "b"; } static void executeAction(Object var) {x += "c"; } static void executeAction(short var) {x += "d"; } static void executeAction(float var) {x += "e"; } static void executeAction(double var) {x += "f"; } }
Bueno Has estudiado el código. ¿Cuál será la conclusión?
- befe
- bfce
- efce
- aecf
La respuesta correcta se da al final del artículo.
Que paso ahora Cómo la JVM compila métodos sobrecargados
Para comprender lo que sucedió en el Listado 2, necesita saber algunas cosas sobre cómo la JVM compila los métodos sobrecargados.
En primer lugar, la JVM es razonablemente perezosa: siempre hará el menor esfuerzo para ejecutar un método. Por lo tanto, cuando piense en cómo JVM maneja la sobrecarga, tenga en cuenta tres características importantes del compilador:
- Ensanchamiento
- Embalaje (autoboxing y unboxing)
- Argumentos de longitud variable (varargs)
Si nunca ha encontrado estas técnicas, algunos ejemplos deberían ayudarlo a comprenderlas. Tenga en cuenta que la JVM los ejecuta en el orden en que se enumeran.
Aquí hay una extensión de ejemplo:
int primitiveIntNumber = 5; double primitiveDoubleNumber = primitiveIntNumber ;
Este es el orden de extensión de los tipos primitivos:

( Nota del traductor: en JLS, la extensión de las primitivas se describe con grandes variaciones, por ejemplo, largo puede expandirse en flotante o doble ) .
Ejemplo de embalaje automático:
int primitiveIntNumber = 7; Integer wrapperIntegerNumber = primitiveIntNumber;
Observe lo que sucede detrás de escena cuando compila el código:
Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber);
Aquí hay un ejemplo de desempaque:
Integer wrapperIntegerNumber = 7; int primitiveIntNumber= wrapperIntegerNumber;
Esto es lo que sucede detrás de escena cuando compila este código:
int primitiveIntNumber = wrapperIntegerNumber.intValue();
Y aquí hay un ejemplo de un método con argumentos de longitud variable. Tenga en cuenta que los métodos de longitud variable son siempre los últimos en ejecutarse.
execute(int... numbers){}
¿Qué son los argumentos de longitud variable?
Los argumentos de longitud variable son solo una matriz de valores dados por tres puntos (...). Podemos pasar tantos números int
a este método.
Por ejemplo:
execute(1,3,4,6,7,8,8,6,4,6,88...);
Los argumentos de longitud variable (varargs) son muy convenientes porque los valores se pueden pasar directamente a un método. Si usáramos matrices, tendríamos que crear una instancia de matriz con valores.
Extensión: estudio de caso
Cuando pasamos el número 1 directamente al método executeAction()
, la JVM lo interpreta automáticamente como int
. Es por eso que este número no se executeAction(short var)
al executeAction(short var)
.
Del mismo modo, si pasamos el número 1.0
JVM reconocerá automáticamente que es el doble.
Por supuesto, el número 1.0
también puede ser float
, pero el tipo de tales literales está predefinido. Por lo tanto, en el Listado 2, se executeAction(double var)
método executeAction(double var)
.
Cuando usamos el contenedor Double
, hay dos opciones: o el número se puede desempaquetar en un tipo primitivo o se puede expandir a Object
. (Recuerde que cada clase en Java extiende la clase Object
). En este caso, la JVM elige una extensión de tipo Double
in Object
, ya que requiere menos esfuerzo que desempaquetar.
El último que pasamos es 1L
y dado que especificamos el tipo, es long
.
Errores de sobrecarga comunes
A estas alturas, probablemente haya entendido que las cosas pueden ser confusas con la sobrecarga de métodos, así que veamos algunos problemas que es probable que encuentre.
Autoboxing con envoltorios
Java es un lenguaje de programación fuertemente tipado y cuando usamos el autoenvoltura con envoltorios, hay algunas cosas que debemos considerar. En primer lugar, el siguiente código no se compila:
int primitiveIntNumber = 7; Double wrapperNumber = primitiveIntNumber;
El autoempaquetado solo funcionará con el tipo double
porque cuando compila el código, será equivalente a esto:
Double number = Double.valueOf(primitiveIntNumber);
Este código se compilará. El primer int
se expandirá a double
y luego se empaquetará en Double
. Pero con el empaquetado automático, no hay extensión de tipo y el constructor Double.valueof
espera un double
, no un int
. En este caso, el empaquetado automático funcionará si hacemos una conversión de tipo explícita, por ejemplo:
Double wrapperNumber = (double) primitiveIntNumber;
Recuerde que Integer
no puede ser Long
y Float
y no puede ser Double
. No hay herencia. Cada uno de estos tipos ( Integer
, Long
, Float
y Double
) es Number
y Object
.
En caso de duda, solo recuerde que los números de contenedor pueden expandirse a Number
u Object
. (Hay mucho más que se puede decir sobre los envoltorios, pero dejémoslo para otro artículo).
Literales de código
Cuando no especificamos el tipo de un número literal, la JVM calculará el tipo por nosotros. Si utilizamos directamente el número 1
en el código, la JVM lo creará como int
. Si intentamos pasar 1
directamente a un método que acepta short
, entonces no se compilará.
Por ejemplo:
class Calculator { public static void main(String... args) {
La misma regla se aplicará cuando 1.0
el número 1.0
. Aunque puede ser un float
, la JVM lo considerará un double
.
class Calculator { public static void main(String... args) {
Otro error común es la suposición de que Double
o cualquier otro contenedor es mejor para un método que obtiene el double
.
El hecho es que la JVM requiere menos esfuerzo para extender el contenedor Double
en un Object
lugar de desempaquetarlo en un tipo double
primitivo.
Para resumir, cuando se usa directamente en código java, 1
será int
y 1.0
será double
. La extensión es la forma más fácil de ejecutar, luego hay empaquetado o desempaquetado y la última operación siempre será métodos de longitud variable.
Como un hecho curioso. ¿Sabes que el tipo char
acepta números?
char anyChar = 127;
Lo que necesita recordar sobre la sobrecarga
La sobrecarga es una técnica muy poderosa para casos en los que necesita el mismo nombre de método con diferentes parámetros. Esta es una técnica útil porque usar los nombres correctos hace que el código sea más fácil de leer. En lugar de duplicar el nombre del método y agregar desorden a su código, simplemente puede sobrecargarlo.
Esto mantiene el código limpio y fácil de leer, y también reduce el riesgo de que los métodos duplicados rompan parte del sistema.
Qué tener en cuenta: al sobrecargar el método, la JVM hará el menor esfuerzo posible.
Aquí está el orden del camino más perezoso para la ejecución:
- El primero se está ampliando.
- El segundo es el boxeo
- Tercero, argumentos de longitud variable (varargs)
Cosas a considerar: surgen situaciones difíciles al declarar números directamente: 1
será int
y 1.0
será double
.
También recuerde que puede declarar estos tipos explícitamente usando la sintaxis 1F
o 1f
para float
y 1D
o 1d
para double
.
Esto concluye el papel de la JVM en la sobrecarga de métodos. Es importante comprender que la JVM es inherentemente perezosa y siempre seguirá el camino más vago.
La respuesta
La respuesta al Listado 2 es la Opción 3. efce.
Obtenga más información sobre la sobrecarga de métodos en Java
Una introducción a clases y objetos para principiantes absolutos, que incluye pequeñas secciones sobre métodos y sobrecarga de métodos.
Obtenga más información acerca de por qué es importante que Java sea un lenguaje fuertemente tipado y conozca los tipos primitivos de Java.
Conozca las limitaciones y desventajas de la sobrecarga de métodos, así como también cómo resolverlos usando tipos personalizados y objetos de parámetros.