Saltar al contenido principal

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 static en 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.