Scope, sobrecarga y static
Los métodos nos permiten separar operaciones. Para usarlos con seguridad necesitamos responder tres preguntas: ¿dónde existe cada variable?, ¿cómo distingue Java métodos con el mismo nombre? y ¿por qué escribimos static en este curso?
Scope: la zona donde existe un nombre
El scope o ámbito es la parte del código desde la que podemos utilizar una variable.
public static void mostrarDoble(int numero) {
int doble = numero * 2;
System.out.println(doble);
}
numero y doble son variables locales del método. Solo existen dentro de mostrarDoble. Otro método no puede referirse directamente a ellas.
En la práctica, una variable local nace cuando se ejecuta su declaración y deja de estar disponible al salir de su bloque.
Scope de bloque
Las llaves crean bloques:
int edad = 20;
if (edad >= 18) {
String mensaje = "Acceso permitido";
System.out.println(mensaje);
}
// System.out.println(mensaje); // no compila
mensaje pertenece al bloque del if. Fuera de sus llaves ya no está en scope.
Lo mismo ocurre con un contador declarado en un for:
for (int numero = 1; numero <= 3; numero++) {
System.out.println(numero);
}
// System.out.println(numero); // no compila
Predice: ¿qué compila?
int total = 0;
for (int numero = 1; numero <= 3; numero++) {
total += numero;
}
System.out.println(total);
Compila y muestra 6: total se declaró fuera del bloque y sigue disponible después; numero no.
Variables distintas en métodos distintos
public static int duplicar(int numero) {
int resultado = numero * 2;
return resultado;
}
public static int triplicar(int numero) {
int resultado = numero * 3;
return resultado;
}
No hay conflicto. Cada numero y cada resultado pertenece a una llamada y a un método diferentes.
Shadowing básico
Hablamos de shadowing cuando un nombre interior oculta temporalmente otro nombre exterior. No podemos redeclarar una variable local dentro de un bloque anidado si sigue en scope:
int puntos = 10;
if (puntos > 0) {
// int puntos = 20; // no compila: puntos ya existe en el método
int puntosExtra = 20;
System.out.println(puntos + puntosExtra);
}
Más adelante, al estudiar objetos, aparecerán otros casos de shadowing. Por ahora evita reutilizar nombres si puede crear dudas.
Sobrecarga: el mismo nombre, distintas entradas
La sobrecarga permite declarar varios métodos con el mismo nombre si su lista de parámetros es distinta:
public static int sumar(int a, int b) {
return a + b;
}
public static double sumar(double a, double b) {
return a + b;
}
public static int sumar(int a, int b, int c) {
return a + b + c;
}
Java selecciona el método según los argumentos:
System.out.println(sumar(2, 3)); // int, int
System.out.println(sumar(2.5, 3.1)); // double, double
System.out.println(sumar(2, 3, 4)); // tres int
La firma que distingue estas versiones incluye el nombre y la lista de tipos de sus parámetros.
Cambiar solo el retorno no basta
Esto no es una sobrecarga válida:
// public static int convertir(int valor) { ... }
// public static double convertir(int valor) { ... }
Ambos reciben un int. Java debe elegir el método antes de saber dónde utilizaremos el resultado, por lo que cambiar solo el retorno no crea una firma distinta.
Práctica inmediata
Sobrecarga mostrar para recibir un String o un int:
public static void mostrar(String texto) {
System.out.println("Texto: " + texto);
}
public static void mostrar(int numero) {
System.out.println("Número: " + numero);
}
Qué significa static aquí
main es static. Un método static pertenece a la clase y puede llamarse directamente desde ese contexto:
public class Conversor {
public static void main(String[] args) {
System.out.println(minutosASegundos(3));
}
public static int minutosASegundos(int minutos) {
return minutos * 60;
}
}
No significa que todos los métodos de Java deban ser static. Lo usamos ahora porque todavía no hemos aprendido a crear y utilizar objetos. Los métodos de instancia y el modelado con clases pertenecen al siguiente nivel.
Programa completo con sobrecarga y scope
public class Formateador {
public static void main(String[] args) {
String etiqueta = formatear("Intentos", 3);
System.out.println(etiqueta);
System.out.println(formatear(7.5));
}
public static String formatear(String nombre, int valor) {
String resultado = nombre + ": " + valor;
return resultado;
}
public static String formatear(double valor) {
String resultado = "Valor decimal: " + valor;
return resultado;
}
}
Las dos variables llamadas resultado no se interfieren: cada una pertenece a su método. La llamada con String, int elige la primera firma; la llamada con double, la segunda.
Errores habituales
- Usar fuera de las llaves una variable declarada dentro.
- Esperar que una variable local conserve su valor entre llamadas independientes.
- Declarar dos veces el mismo nombre en scopes locales que se solapan.
- Creer que el nombre de los parámetros distingue una sobrecarga: importan sus tipos y orden.
- Intentar sobrecargar cambiando solo el retorno.
- Convertir
staticen una regla universal en vez de entenderlo como la herramienta temporal de este curso.
Ejercicio: conversor sobrecargado
Crea dos versiones de convertirAHoras:
- una recibe minutos como
int; - otra recibe horas y minutos como dos
int.
Casos: convertirAHoras(90) debe devolver 1.5; convertirAHoras(1, 30) también debe devolver 1.5.
Solución razonada
public class ConversorTiempo {
public static void main(String[] args) {
System.out.println(convertirAHoras(90));
System.out.println(convertirAHoras(1, 30));
}
public static double convertirAHoras(int minutos) {
return minutos / 60.0;
}
public static double convertirAHoras(int horas, int minutos) {
int minutosTotales = horas * 60 + minutos;
return convertirAHoras(minutosTotales);
}
}
La segunda versión compone la primera. minutosTotales solo existe durante esa llamada. A continuación aplicaremos métodos y bucles al procesamiento de texto.