Métodos: declaración, parámetros y retorno
Introducción
Los métodos son bloques de código que realizan una tarea específica y constituyen el comportamiento de los objetos en Java. Funcionan de manera similar a las funciones en otros lenguajes de programación, pero están asociados a una clase u objeto. Los métodos permiten organizar el código en unidades lógicas reutilizables, mejorando así la legibilidad y mantenimiento del programa. En este artículo, profundizaremos en cómo declarar métodos, trabajar con diferentes tipos de parámetros y gestionar los valores de retorno.
Declaración de métodos
La estructura básica para declarar un método en Java es la siguiente:
[modificadores] tipoRetorno nombreMetodo([parámetros]) {
// Cuerpo del método
[return valorRetorno;]
}
Donde:
- modificadores: son opcionales y pueden incluir
public
,private
,protected
,static
, etc. - tipoRetorno: especifica el tipo de dato que devuelve el método (o
void
si no devuelve nada) - nombreMetodo: es el identificador con el que llamaremos al método
- parámetros: lista opcional de variables que recibe el método
- cuerpo: contiene las instrucciones que se ejecutarán cuando se llame al método
- return: devuelve un valor del tipo especificado (opcional si el método es
void
)
Veamos un ejemplo sencillo:
public class Calculadora {
// Método que suma dos números enteros
public int sumar(int a, int b) {
int resultado = a + b;
return resultado;
}
// Método que muestra un mensaje (no devuelve valor)
public void mostrarMensaje(String mensaje) {
System.out.println("Mensaje: " + mensaje);
}
}
Convenciones de nombres para métodos
En Java, se siguen ciertas convenciones para nombrar los métodos:
- Los nombres de métodos comienzan con letra minúscula
- Se utiliza la notación "camelCase" (primera palabra en minúscula, las siguientes con la primera letra en mayúscula)
- Los nombres deben ser verbos o frases verbales que indiquen la acción que realiza el método
Ejemplos correctos:
calcularTotal
obtenerNombre
esValido
convertirAMinusculas
Parámetros de métodos
Los parámetros son variables que se definen en la declaración del método y permiten pasar datos al método cuando se llama. Los parámetros pueden ser de cualquier tipo de dato en Java, incluyendo tipos primitivos y objetos.
Tipos de parámetros
Parámetros de tipo primitivo
Cuando pasamos tipos de datos primitivos (int, double, boolean, etc.), se pasa una copia del valor:
public class EjemploParametros {
public void incrementar(int numero) {
numero = numero + 1;
System.out.println("Dentro del método: " + numero);
}
public static void main(String[] args) {
EjemploParametros ejemplo = new EjemploParametros();
int valor = 5;
ejemplo.incrementar(valor);
System.out.println("Después de llamar al método: " + valor);
}
}
Salida:
Dentro del método: 6
Después de llamar al método: 5
Como vemos, el valor original no se modifica porque se pasó una copia.
Parámetros de tipo referencia
Cuando pasamos objetos como parámetros, se pasa la referencia (la dirección de memoria), lo que permite modificar el objeto original:
public class Persona {
String nombre;
public Persona(String nombre) {
this.nombre = nombre;
}
}
public class EjemploReferencia {
public void cambiarNombre(Persona p) {
p.nombre = "Nuevo nombre";
System.out.println("Dentro del método: " + p.nombre);
}
public static void main(String[] args) {
Persona persona = new Persona("Juan");
System.out.println("Antes de llamar al método: " + persona.nombre);
EjemploReferencia ejemplo = new EjemploReferencia();
ejemplo.cambiarNombre(persona);
System.out.println("Después de llamar al método: " + persona.nombre);
}
}
Salida:
Antes de llamar al método: Juan
Dentro del método: Nuevo nombre
Después de llamar al método: Nuevo nombre
En este caso, el objeto original se modifica porque se pasó su referencia.
Cantidad variable de parámetros (varargs)
Java permite que un método acepte una cantidad variable de parámetros del mismo tipo utilizando la sintaxis ...
(tres puntos):
public class Calculadora {
// Método que suma una cantidad variable de números
public int sumar(int... numeros) {
int suma = 0;
for (int num : numeros) {
suma += num;
}
return suma;
}
public static void main(String[] args) {
Calculadora calc = new Calculadora();
System.out.println(calc.sumar(5, 10));
System.out.println(calc.sumar(2, 4, 6, 8, 10));
System.out.println(calc.sumar()); // Sin argumentos
}
}
Salida:
15
30
0
Importante: el parámetro varargs debe ser el último en la lista de parámetros.
Parámetros con valores por defecto
A diferencia de otros lenguajes, Java no admite valores por defecto para parámetros. Sin embargo, podemos simular este comportamiento utilizando sobrecarga de métodos:
public class Mensaje {
// Método con todos los parámetros
public void enviar(String destinatario, String asunto, String cuerpo) {
System.out.println("Enviando mensaje a: " + destinatario);
System.out.println("Asunto: " + asunto);
System.out.println("Cuerpo: " + cuerpo);
}
// Método sobrecargado con parámetros "por defecto"
public void enviar(String destinatario, String asunto) {
// Utiliza un valor por defecto para el cuerpo
enviar(destinatario, asunto, "(Sin contenido)");
}
// Otro método sobrecargado con más valores "por defecto"
public void enviar(String destinatario) {
// Utiliza valores por defecto para asunto y cuerpo
enviar(destinatario, "(Sin asunto)", "(Sin contenido)");
}
}
Valores de retorno
El valor de retorno es el resultado que el método devuelve cuando finaliza su ejecución. Se especifica con la palabra clave return
.
Tipos de retorno
Un método puede devolver:
- Un tipo primitivo (int, double, boolean, etc.)
- Un objeto (String, arrays, clases personalizadas, etc.)
- void (no devuelve nada)
public class TiposRetorno {
// Retorno de tipo primitivo
public int sumar(int a, int b) {
return a + b;
}
// Retorno de tipo objeto
public String formatearNombre(String nombre, String apellido) {
return apellido.toUpperCase() + ", " + nombre;
}
// Retorno de tipo array
public int[] generarSecuencia(int inicio, int fin) {
int longitud = fin - inicio + 1;
int[] secuencia = new int[longitud];
for (int i = 0; i < longitud; i++) {
secuencia[i] = inicio + i;
}
return secuencia;
}
// Método sin valor de retorno
public void mostrarInformacion(String info) {
System.out.println("Información: " + info);
// No necesita return
}
}
La instrucción return
La instrucción return
tiene dos funciones:
- Devolver un valor (si el método no es
void
) - Finalizar la ejecución del método
public int buscarNumero(int[] array, int numero) {
for (int i = 0; i < array.length; i++) {
if (array[i] == numero) {
return i; // Devuelve el índice y termina el método
}
}
return -1; // Si no encuentra el número, devuelve -1
}
Un método puede tener múltiples instrucciones return
, pero cuando se ejecuta una, el método finaliza inmediatamente:
public String evaluarCalificacion(int puntuacion) {
if (puntuacion < 0 || puntuacion > 100) {
return "Puntuación inválida";
}
if (puntuacion >= 90) {
return "Sobresaliente";
} else if (puntuacion >= 70) {
return "Notable";
} else if (puntuacion >= 60) {
return "Bien";
} else if (puntuacion >= 50) {
return "Aprobado";
} else {
return "Suspenso";
}
}
Sobrecarga de métodos
La sobrecarga de métodos permite definir varios métodos con el mismo nombre pero con diferentes parámetros. Java determina cuál método invocar basándose en los argumentos proporcionados.
public class Matematicas {
// Método que suma dos enteros
public int sumar(int a, int b) {
return a + b;
}
// Método sobrecargado que suma tres enteros
public int sumar(int a, int b, int c) {
return a + b + c;
}
// Método sobrecargado que suma dos doubles
public double sumar(double a, double b) {
return a + b;
}
// Método sobrecargado que concatena dos strings
public String sumar(String a, String b) {
return a + b;
}
}
La sobrecarga se determina por:
- Número de parámetros
- Tipos de parámetros
- Orden de los tipos de parámetros
No se puede sobrecargar un método basándose solo en el tipo de retorno o en los modificadores.
Métodos especiales
Métodos estáticos (de clase)
Los métodos estáticos pertenecen a la clase, no a los objetos. Se pueden llamar sin crear una instancia de la clase:
public class Utilidades {
public static double calcularIVA(double precio) {
return precio * 0.21;
}
public static void main(String[] args) {
// Llamada a método estático sin crear objeto
double iva = Utilidades.calcularIVA(100);
System.out.println("IVA: " + iva);
}
}
Métodos de instancia vs métodos de clase
- Métodos de instancia: Operan sobre un objeto específico y pueden acceder a los atributos de instancia.
- Métodos de clase (estáticos): Pertenecen a la clase y no pueden acceder directamente a los atributos de instancia ni utilizar
this
.
public class Contador {
// Atributo de instancia
private int valor;
// Atributo de clase
private static int contadorGlobal = 0;
public Contador() {
valor = 0;
contadorGlobal++;
}
// Método de instancia
public void incrementar() {
valor++; // Accede a atributo de instancia
}
// Método de instancia
public int getValor() {
return valor; // Accede a atributo de instancia
}
// Método de clase (estático)
public static int getContadorGlobal() {
return contadorGlobal; // Accede a atributo estático
// No puede acceder a 'valor' ni usar 'this'
}
}
Buenas prácticas al trabajar con métodos
- Responsabilidad única: Cada método debe realizar una sola tarea bien definida.
- Tamaño reducido: Los métodos deben ser cortos y concisos (normalmente menos de 20 líneas).
- Nombres descriptivos: El nombre del método debe describir claramente su propósito.
- Comentarios javadoc: Documentar los métodos con comentarios javadoc que expliquen su propósito, parámetros y valor de retorno.
- Minimizar efectos secundarios: Evitar que un método modifique variables globales o estado externo de forma inesperada.
- Evitar código duplicado: Si el mismo código se repite, extraerlo a un método separado.
- Validar parámetros: Comprobar que los parámetros recibidos son válidos al inicio del método.
Ejemplo completo
Veamos un ejemplo completo que ilustra varios conceptos relacionados con métodos:
/**
* Clase que representa una cuenta bancaria.
*/
public class CuentaBancaria {
private String titular;
private String numeroCuenta;
private double saldo;
private static final double COMISION_TRANSFERENCIA = 0.01; // 1%
/**
* Constructor de la clase CuentaBancaria.
* @param titular Nombre del titular de la cuenta
* @param numeroCuenta Número de la cuenta bancaria
* @param saldoInicial Saldo inicial de la cuenta
*/
public CuentaBancaria(String titular, String numeroCuenta, double saldoInicial) {
this.titular = titular;
this.numeroCuenta = numeroCuenta;
// Validamos que el saldo inicial sea positivo
if (saldoInicial >= 0) {
this.saldo = saldoInicial;
} else {
this.saldo = 0;
System.out.println("El saldo inicial no puede ser negativo. Se establece a 0.");
}
}
/**
* Método sobrecargado que crea una cuenta con saldo inicial 0.
* @param titular Nombre del titular de la cuenta
* @param numeroCuenta Número de la cuenta bancaria
*/
public CuentaBancaria(String titular, String numeroCuenta) {
this(titular, numeroCuenta, 0); // Llamada al constructor principal
}
/**
* Deposita una cantidad en la cuenta.
* @param cantidad Importe a depositar
* @return true si el depósito se realizó con éxito, false en caso contrario
*/
public boolean depositar(double cantidad) {
if (cantidad > 0) {
saldo += cantidad;
System.out.println("Depósito de " + cantidad + "€ realizado. Nuevo saldo: " + saldo + "€");
return true;
} else {
System.out.println("Error: La cantidad a depositar debe ser positiva.");
return false;
}
}
/**
* Retira una cantidad de la cuenta.
* @param cantidad Importe a retirar
* @return true si la retirada se realizó con éxito, false en caso contrario
*/
public boolean retirar(double cantidad) {
if (cantidad <= 0) {
System.out.println("Error: La cantidad a retirar debe ser positiva.");
return false;
}
if (saldo >= cantidad) {
saldo -= cantidad;
System.out.println("Retirada de " + cantidad + "€ realizada. Nuevo saldo: " + saldo + "€");
return true;
} else {
System.out.println("Error: Saldo insuficiente para realizar la operación.");
return false;
}
}
/**
* Transfiere una cantidad a otra cuenta.
* @param cuentaDestino Cuenta a la que se transfiere el dinero
* @param cantidad Importe a transferir
* @return true si la transferencia se realizó con éxito, false en caso contrario
*/
public boolean transferir(CuentaBancaria cuentaDestino, double cantidad) {
if (cantidad <= 0) {
System.out.println("Error: La cantidad a transferir debe ser positiva.");
return false;
}
double comision = calcularComision(cantidad);
double totalADescontar = cantidad + comision;
if (saldo >= totalADescontar) {
// Retiramos el dinero de esta cuenta
saldo -= totalADescontar;
// Depositamos en la cuenta destino
cuentaDestino.saldo += cantidad;
System.out.println("Transferencia de " + cantidad + "€ realizada a la cuenta " +
cuentaDestino.numeroCuenta);
System.out.println("Comisión: " + comision + "€");
System.out.println("Nuevo saldo: " + saldo + "€");
return true;
} else {
System.out.println("Error: Saldo insuficiente para realizar la transferencia.");
return false;
}
}
/**
* Calcula la comisión por transferencia.
* @param cantidad Importe sobre el que calcular la comisión
* @return Importe de la comisión
*/
private double calcularComision(double cantidad) {
return cantidad * COMISION_TRANSFERENCIA;
}
/**
* Muestra la información de la cuenta.
*/
public void mostrarInformacion() {
System.out.println("===== INFORMACIÓN DE CUENTA =====");
System.out.println("Titular: " + titular);
System.out.println("Número de cuenta: " + numeroCuenta);
System.out.println("Saldo actual: " + saldo + "€");
System.out.println("===============================");
}
/**
* Obtiene el saldo actual de la cuenta.
* @return Saldo de la cuenta
*/
public double getSaldo() {
return saldo;
}
/**
* Obtiene el titular de la cuenta.
* @return Nombre del titular
*/
public String getTitular() {
return titular;
}
/**
* Obtiene el número de cuenta.
* @return Número de cuenta
*/
public String getNumeroCuenta() {
return numeroCuenta;
}
/**
* Método estático que calcula el interés generado por un capital
* a lo largo de un período determinado.
* @param capital Capital inicial
* @param tasaInteres Tasa de interés anual (en porcentaje)
* @param años Número de años
* @return Interés generado
*/
public static double calcularInteres(double capital, double tasaInteres, int años) {
return capital * (tasaInteres / 100) * años;
}
}
Y una clase principal para probar estos métodos:
public class PruebaCuentaBancaria {
public static void main(String[] args) {
// Crear cuentas bancarias
CuentaBancaria cuenta1 = new CuentaBancaria("Carlos Martínez", "ES12345678", 1000);
CuentaBancaria cuenta2 = new CuentaBancaria("Laura Pérez", "ES87654321");
// Mostrar información inicial
System.out.println("Estado inicial de las cuentas:");
cuenta1.mostrarInformacion();
cuenta2.mostrarInformacion();
// Realizar operaciones
cuenta1.depositar(500);
cuenta2.depositar(200);
cuenta1.retirar(300);
cuenta1.transferir(cuenta2, 400);
// Mostrar información final
System.out.println("\nEstado final de las cuentas:");
cuenta1.mostrarInformacion();
cuenta2.mostrarInformacion();
// Uso de método estático
double interesGenerado = CuentaBancaria.calcularInteres(cuenta1.getSaldo(), 2.5, 1);
System.out.println("\nInterés generado en un año para la cuenta de " +
cuenta1.getTitular() + ": " + interesGenerado + "€");
}
}
La salida de este programa sería similar a:
Estado inicial de las cuentas:
===== INFORMACIÓN DE CUENTA =====
Titular: Carlos Martínez
Número de cuenta: ES12345678
Saldo actual: 1000.0€
===============================
===== INFORMACIÓN DE CUENTA =====
Titular: Laura Pérez
Número de cuenta: ES87654321
Saldo actual: 0.0€
===============================
Depósito de 500.0€ realizado. Nuevo saldo: 1500.0€
Depósito de 200.0€ realizado. Nuevo saldo: 200.0€
Retirada de 300.0€ realizada. Nuevo saldo: 1200.0€
Transferencia de 400.0€ realizada a la cuenta ES87654321
Comisión: 4.0€
Nuevo saldo: 796.0€
Estado final de las cuentas:
===== INFORMACIÓN DE CUENTA =====
Titular: Carlos Martínez
Número de cuenta: ES12345678
Saldo actual: 796.0€
===============================
===== INFORMACIÓN DE CUENTA =====
Titular: Laura Pérez
Número de cuenta: ES87654321
Saldo actual: 600.0€
===============================
Interés generado en un año para la cuenta de Carlos Martínez: 19.9€
Métodos recursivos
Los métodos recursivos son aquellos que se llaman a sí mismos para resolver un problema. Son útiles para ciertos tipos de algoritmos y cálculos matemáticos.
public class Factorial {
/**
* Calcula el factorial de un número de forma recursiva.
* @param n Número del que calcular el factorial
* @return El factorial de n
*/
public static long calcularFactorial(int n) {
// Caso base
if (n == 0 || n == 1) {
return 1;
}
// Caso recursivo
else {
return n * calcularFactorial(n - 1);
}
}
public static void main(String[] args) {
int numero = 5;
long resultado = calcularFactorial(numero);
System.out.println("El factorial de " + numero + " es: " + resultado);
}
}
Al llamar a calcularFactorial(5)
, el método se ejecutará de la siguiente manera:
calcularFactorial(5)
= 5 *calcularFactorial(4)
calcularFactorial(4)
= 4 *calcularFactorial(3)
calcularFactorial(3)
= 3 *calcularFactorial(2)
calcularFactorial(2)
= 2 *calcularFactorial(1)
calcularFactorial(1)
= 1 (caso base)
Por lo tanto: calcularFactorial(5)
= 5 * 4 * 3 * 2 * 1 = 120
Resumen
En este artículo, hemos explorado en profundidad los métodos en Java: cómo declararlos, trabajar con distintos tipos de parámetros y gestionar valores de retorno. Hemos aprendido sobre la sobrecarga de métodos, métodos estáticos y recursivos, y buenas prácticas para su implementación. Los métodos son fundamentales en la programación orientada a objetos, ya que encapsulan comportamiento y permiten la reutilización de código. En el próximo artículo, estudiaremos los constructores y cómo se gestionan los objetos en memoria, lo que complementará tu comprensión de las clases y objetos en Java.