Métodos en Java
En esta lección
- Estructura de un método
- Llamar a un método
- void: métodos que no devuelven nada
- Los tipos deben encajar
- Sobrecarga
- Paso por valor
- Varargs: número variable de argumentos
- Ámbito de las variables
- Documentar con Javadoc
- Métodos pequeños y con un solo propósito
- ¿Qué significa public static void main?
- Errores frecuentes
- Resumen
Un método es un bloque de código con nombre que realiza una tarea concreta. Es lo que en otros lenguajes se llama función; en Java se llaman métodos porque siempre viven dentro de una clase. Gracias a ellos escribes una operación una sola vez y la usas donde quieras, divides un programa grande en piezas pequeñas y fáciles de entender, y puedes probar cada pieza por separado. En esta lección aprenderás a crearlos, a pasarles datos, a devolver resultados y a entender qué ocurre con los parámetros por dentro.
Estructura de un método
public static int sumar(int a, int b) {
return a + b;
}
La primera línea se llama firma o cabecera del método:
public static: por ahora, ponlo en todos tus métodos. Más abajo verás qué significa.int: el tipo de retorno, es decir, qué tipo de dato devuelve el método.sumar: el nombre, en camelCase y normalmente con un verbo (calcular,mostrar,esValido).(int a, int b): los parámetros, cada uno con su tipo, separados por comas.return: devuelve el resultado y termina el método.
Llamar a un método
public class Calculadora {
public static int sumar(int a, int b) {
return a + b;
}
public static void main(String[] args) {
int resultado = sumar(4, 6);
System.out.println(resultado); // 10
System.out.println(sumar(100, 23)); // 123
System.out.println(sumar(sumar(1, 2), 3)); // 6
}
}
Los métodos se escriben dentro de la clase y fuera de main, uno detrás de otro. El orden no importa: main puede llamar a un método escrito debajo.
Los valores que pasas al llamar (4 y 6) se llaman argumentos; las variables que los reciben (a y b), parámetros. Deben coincidir en número, orden y tipo.
void: métodos que no devuelven nada
Si el método solo hace algo (mostrar un mensaje, por ejemplo) y no devuelve ningún valor, su tipo es void:
public static void saludar(String nombre) {
System.out.println("¡Hola, " + nombre + "!");
}
saludar("Marta"); // ¡Hola, Marta!
Un método void puede usar return; sin valor para terminar antes de tiempo:
public static void mostrarNota(double nota) {
if (nota < 0 || nota > 10) {
System.out.println("Nota no válida");
return; // sale del método
}
System.out.println("Tu nota es " + nota);
}
Los tipos deben encajar
Java comprueba que todo cuadre:
public static double calcularMedia(int[] notas) {
int suma = 0;
for (int nota : notas) {
suma += nota;
}
return (double) suma / notas.length;
}
- Si el método dice que devuelve
double, todos sus caminos deben terminar en unreturncon undouble. Si en unifte olvidas delreturn, obtienes el error missing return statement. - Si llamas a
calcularMedia("hola"), el programa no compila: se esperaba un array deint.
Los métodos que devuelven boolean suelen llamarse es... o tiene... y hacen que las condiciones se lean casi como una frase:
public static boolean esPar(int n) {
return n % 2 == 0;
}
if (esPar(10)) {
System.out.println("Par"); // Par
}
Fíjate en que no hace falta escribir if (n % 2 == 0) return true; else return false;: la comparación ya es un boolean.
Sobrecarga
Puedes tener varios métodos con el mismo nombre si reciben parámetros distintos (en número o en tipo). Java elige cuál usar según lo que le pases:
public static int area(int lado) {
return lado * lado; // cuadrado
}
public static int area(int base, int altura) {
return base * altura; // rectángulo
}
public static double area(double radio) {
return Math.PI * radio * radio; // círculo
}
System.out.println(area(5)); // 25
System.out.println(area(4, 3)); // 12
System.out.println(area(1.0)); // 3.141592653589793
El tipo de retorno no cuenta para la sobrecarga: dos métodos que solo se diferencian en lo que devuelven no compilan. System.out.println es el ejemplo más famoso de sobrecarga: hay una versión para int, otra para double, otra para String…
Paso por valor
Esta es una de las ideas más importantes de la lección, y fuente de muchas dudas. En Java, los argumentos siempre se pasan por valor: el método recibe una copia de lo que hay en la variable. Lo que cambia es qué hay dentro de esa variable.
Con primitivos: el original no cambia
public class PasoPorValor {
public static void duplicar(int numero) {
numero = numero * 2;
System.out.println("Dentro: " + numero); // Dentro: 20
}
public static void main(String[] args) {
int puntos = 10;
duplicar(puntos);
System.out.println("Fuera: " + puntos); // Fuera: 10
}
}
numero es una copia de puntos. Cambiar la copia no afecta al original. Si quieres que el cambio llegue fuera, devuelve el valor y guárdalo:
public static int duplicar(int numero) {
return numero * 2;
}
int puntos = 10;
puntos = duplicar(puntos);
System.out.println(puntos); // 20
Con arrays y objetos: se copia la referencia
Como viste en variables, una variable de tipo array u objeto no guarda los datos, sino la dirección donde están. Al pasarla a un método se copia esa dirección, así que el parámetro apunta al mismo array. Si el método modifica su contenido, el cambio se ve fuera:
import java.util.Arrays;
public class SubirNotas {
public static void subirUnPunto(int[] notas) {
for (int i = 0; i < notas.length; i++) {
notas[i] = Math.min(notas[i] + 1, 10);
}
}
public static void main(String[] args) {
int[] clase = {4, 7, 10};
subirUnPunto(clase);
System.out.println(Arrays.toString(clase)); // [5, 8, 10]
}
}
Pero si dentro del método asignas un array nuevo al parámetro, solo cambias la copia de la dirección; el original sigue intacto:
public static void reiniciar(int[] notas) {
notas = new int[] {0, 0, 0}; // el parámetro apunta a otro array
}
int[] clase = {4, 7, 10};
reiniciar(clase);
System.out.println(Arrays.toString(clase)); // [4, 7, 10]
| Qué pasas | Qué recibe el método | ¿Puede cambiar el original? |
|---|---|---|
Primitivo (int, double…) | Copia del valor | No |
| Array u objeto | Copia de la referencia | Su contenido, sí; la variable original, no |
String | Copia de la referencia | No, porque los String son inmutables |
Varargs: número variable de argumentos
A veces no sabes cuántos valores recibirá un método. Con ... detrás del tipo, el método acepta cero o más argumentos, que dentro se tratan como un array:
public static int sumarTodos(int... numeros) {
int total = 0;
for (int n : numeros) {
total += n;
}
return total;
}
System.out.println(sumarTodos()); // 0
System.out.println(sumarTodos(5)); // 5
System.out.println(sumarTodos(1, 2, 3, 4)); // 10
System.out.println(sumarTodos(new int[] {10, 20})); // 30
Reglas: solo puede haber un parámetro varargs y debe ser el último (mostrar(String titulo, int... valores)). String.format y printf usan varargs; por eso admiten tantos valores como marcadores.
Ámbito de las variables
El ámbito de una variable es la zona del código donde existe. En Java la regla es sencilla: una variable existe desde que se declara hasta la llave } que cierra su bloque.
public class Ambito {
public static int calcularTotal(int unidades) {
double precio = 2.5; // solo existe en calcularTotal
int total = (int) (unidades * precio);
return total;
}
public static void main(String[] args) {
int total = calcularTotal(4); // otra variable distinta, de main
System.out.println(total); // 10
for (int i = 0; i < 3; i++) {
int doble = i * 2; // solo existe dentro del for
}
// System.out.println(i); // no compila: i ya no existe
// System.out.println(precio); // no compila: es de otro método
}
}
- Los parámetros y las variables declaradas dentro de un método son locales: nacen al llamarlo y desaparecen al terminar.
- Dos métodos pueden tener variables con el mismo nombre sin que se mezclen.
- Un método no puede ver las variables locales de otro. Si necesita un dato, pásaselo como parámetro.
Esto es bueno: cada método es una caja cerrada que solo se comunica por sus parámetros y su return. Las variables que pertenecen a toda la clase (atributos) las verás en clases y objetos.
Documentar con Javadoc
Un comentario Javadoc (/** ... */) justo encima de un método explica qué hace, qué recibe y qué devuelve. Los IDE lo muestran al pasar el ratón por encima de cualquier llamada, y la herramienta javadoc genera con él páginas HTML de documentación:
/**
* Calcula el precio final aplicando un descuento.
*
* @param precio precio original en euros; debe ser positivo
* @param descuento porcentaje de descuento, de 0 a 100
* @return el precio con el descuento aplicado
*/
public static double aplicarDescuento(double precio, int descuento) {
return precio - precio * descuento / 100.0;
}
System.out.println(aplicarDescuento(80, 25)); // 60.0
Las etiquetas más usadas son @param (una por parámetro), @return y @throws (qué excepción puede lanzar).
Métodos pequeños y con un solo propósito
Un buen método hace una sola cosa y su nombre dice exactamente cuál. Compara un main que lo hace todo con otro que delega:
public class Tienda {
public static void main(String[] args) {
double[] carrito = {12.5, 3.99, 20.0};
double subtotal = sumar(carrito);
double total = aplicarIva(subtotal);
mostrarTicket(subtotal, total);
}
static double sumar(double[] precios) {
double suma = 0;
for (double p : precios) {
suma += p;
}
return suma;
}
static double aplicarIva(double importe) {
return importe * 1.21;
}
static void mostrarTicket(double subtotal, double total) {
System.out.printf("Subtotal: %.2f%n", subtotal);
System.out.printf("Total con IVA: %.2f%n", total);
}
}
Subtotal: 36.49
Total con IVA: 44.15
main se lee como un índice de lo que hace el programa. Algunas pautas:
- Si no encuentras un nombre claro para un método, probablemente hace demasiadas cosas.
- Si un método pasa de 20-30 líneas, busca partes que puedas extraer.
- Evita que un método calcule y muestre: el que calcula y devuelve un valor se puede reutilizar y probar; el que imprime, no.
¿Qué significa public static void main?
Ahora ya puedes leerlo entero:
public: el método se puede usar desde fuera de la clase. La JVM necesita acceder a él para arrancar el programa. Los modificadores de acceso se explican en paquetes.static: pertenece a la clase y no a un objeto concreto, así que se puede ejecutar sin crear objetos (lo verás en encapsulación, static y final).void: no devuelve nada.main: el nombre que la JVM busca para empezar.String[] args: un array con los argumentos que se pasan al ejecutar el programa desde la terminal (java Programa uno dos).
Errores frecuentes
- Escribir un método dentro de
main: no compila; van al mismo nivel, dentro de la clase. - Olvidar el
returnen algún camino de un método que no esvoid: missing return statement. - Ignorar el valor devuelto:
duplicar(puntos);no cambiapuntossi no guardas el resultado. - Esperar que un método cambie un primitivo que recibe como parámetro.
- Argumentos en otro orden o tipo:
aplicarDescuento(25, 80)compila, pero calcula otra cosa.
Resumen
- Un método tiene tipo de retorno, nombre, parámetros y cuerpo;
returndevuelve el valor. voidindica que no devuelve nada.- La sobrecarga permite varios métodos con el mismo nombre y distintos parámetros.
- Java pasa siempre por valor: con primitivos se copia el valor; con arrays y objetos, la referencia, así que su contenido sí puede cambiar.
tipo... nombre(varargs) acepta un número variable de argumentos.- Las variables locales solo existen dentro de su bloque.
- Documenta con Javadoc y escribe métodos pequeños con un solo propósito.
Un método también puede llamarse a sí mismo. Esa técnica, muy potente, es la recursividad.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.