Funciones y POO
Antes de pasar a la fase final de nuestro plan, necesitas entender un par de cosas más. Me refiero a las funciones y los ejercicios de programación orientada a objetos. A estas alturas ya dominas la sintaxis, pero programas de una forma lineal. Solo hemos hecho programas dentro de la clase Main. En los programas reales es imposible encontrarnos con esa linealidad. Los programas reutilizan código y este se almacena en distintas clases. Después, desde la clase Main se van llamando y ejecutando dependiendo del orden en el que los necesitemos.
Vamos a aprender a reutilizar código y a trabajar con distintas clases; para ello, empezaremos con las funciones y los métodos. En Java, la terminología para «métodos» y «funciones» puede ser un poco confusa, especialmente si vienes de otros lenguajes de programación. En términos de Java:
Método: Es una función que está definida dentro de una clase. En Java, todas las funciones deben estar dentro de una clase, por lo que todas las funciones en Java son métodos. El término «método» es más común en Java.
Función: Es un término más general y se usa en muchos lenguajes de programación para referirse a un bloque de código que realiza una tarea específica. En Java, se refiere a lo mismo que un método, pero se utiliza con menos frecuencia.
Diferencia entre public, protected o private
public: Un método o variable que es declarado como public se puede acceder desde cualquier otra clase. No importa el paquete en el que se encuentre. Lo utilizamos cuando queremos que el método o la variable estén disponibles globalmente.
Cuando un método o variable está declarada como private, entonces solamente será accesible dentro de la misma clase en la que se define. Se utiliza para proteger los datos de un acceso externo.
Por último, un método o variable protected es accesible dentro del mismo paquete y por subclases, incluso si están en paquetes diferentes.
Diferencia entre métodos que devuelve un valor y métodos “void”
- Métodos que devuelven un valor: Estos métodos especifican un tipo de retorno (por ejemplo, int, String, boolean, etc.) en su declaración. Deben utilizar la palabra clave return para devolver un valor del tipo especificado. Son útiles cuando se necesita procesar datos y devolver un resultado.
- Métodos void: Estos métodos usan la palabra clave void para indicar que no devolverán ningún valor.
Reutilizando Métodos estáticos dentro de la clase Main
- Empezaremos con un ejemplo sencillo. La idea es crear unas líneas de código que sumen dos a un número. Escribiré el código y después lo analizaré poco a poco.
public class Main {
public static void sumaDos(int numero) {
int resultado = numero + 2;
System.out.println("El resultado de sumar 2 a " + numero + " es: " + resultado);
}
public static void main(String[] args) {
sumaDos(5);
}
}
Vamos con la explicación. En negro está resaltado nuestro método. Pero, antes de nada, vemos la línea con la que se hace referencia a la clase en la que estamos trabajando. Como comenté en los primeros párrafos del tema, todos los métodos deben ir dentro de una clase; si no, el programa nos va a devolver un mensaje de error. En este caso, la clase Main.
Pero, por otro lado, va encima de el método principal de la clase:
public static void main(String[] args) {
}
Por lo tanto, esta clase tendrá dos métodos, pero solo se ejecuta el código que está dentro del principal. Por eso, si queremos utilizar un método secundario, debemos llamarlo desde el principal. En este ejemplo, hemos creado un método secundario, pero podría haber tantos como sea necesario.
Dentro del método principal, public static void main(String[] args), vemos como se está llamando a al método sumaDos:
sumaDos(5);
Colocamos el número al que queremos que se le sume dos entre paréntesis. Tal como hemos creado el método, es obligatorio que introduzcamos un valor en el paréntesis, ya que lo hemos especificado en el código al escribir (int numero). Pero no siempre vamos a necesitar indicar un valor; eso nos lleva al siguiente ejemplo.
- Ahora vamos a crear el mismo ejemplo, pero queremos que, cuando llamemos al método, nos pida por teclado el número que queremos introducir y que después nos muestre cuál es la suma de esa cifra más dos.
import java.util.Scanner;
public class Main {
public static void sumaDos() {
Scanner scanner = new Scanner(System.in);
System.out.print("Introduce un número: ");
int numero = scanner.nextInt();
int resultado = numero + 2;
System.out.println("El resultado de sumar 2 a " + numero + " es: " + resultado);
}
public static void main(String[] args) {
sumaDos();
}
}
Igual que antes, escribimos nuestro método dentro de la clase Main y antes del método principal. En este caso, hemos creado un código que nos pide que introduzcamos un número por teclado y el programa sumará 2 a este valor y, además, mostrará por pantalla el resultado.
- Imaginemos ahora que, cada cierto tiempo, tenemos que introducir un texto bastante largo en nuestro código. Podríamos escribirlo todas las veces, pero lo más elegante y ordenado es crear un método para reutilizar ese texto. Lo podríamos hacer tal y como te muestro a continuación.
public class Main {
public static void texto() {
System.out.println("A veces un texto puede ser demasiado largo como para que estemos utilizándolo una y otra vez");
System.out.println("Es mejor crear un método y después llamarlo cada vez que necesitemos que se muestre dicho texto.");
System.out.println("Tu programa quedará mucho más limpio y ordenado");
}
public static void main(String[] args) {
texto();
}
}
En el código anterior, hemos visto un método que básicamente consiste en tres líneas de texto. En el momento y en cualquier lugar donde necesitemos que esas líneas aparezcan, simplemente tenemos que llamar a nuestro método y así evitaremos tener grandes textos mezclados con la sintaxis.
- Como último ejemplo (De momento) vamos a ver un método en el que pedimos dos números y nos devuelve la multiplicación de los mismos.
import java.util.Scanner;
public class Main {
public static int producto(int a, int b) {
int c = a * b;
System.out.println("El producto de a * b = " + c);
return c;
}
public static void main(String[] args) {
producto(4, 8);
}
}
Con estos 4 ejemplos hemos visto 3 funciones void y la última que devuelve un valor. Por eso cuando la creamos en vez de static void utilizamos static int. Además si te has fijado en los tres ejemplos void, en el primero entre paréntesis metimos una variable: public static void sumaDos(int numero). En los otros dos no. Esto se debe a que en el primero queríamos escribir nosotros mismos la cifra manualmente. Sin embargo, en el segundo, el programa no necesitaba de ningún dato ya que iba a empezar a ejecutarse y a pedirnos el mismo que introdujéramos un dato por teclado. En cuanto al ejercicio del texto tampoco teníamos que especificar ningún dato, el método siempre muestra el mismo texto.
Reutilizando métodos creados en otras clases distintas de la clase Main
Lo primero que vamos a hacer es crear una clase que llamaremos ejercicios. Inicialmente tendrá este aspecto antes de que empecemos a escribir nuestro texto:
public class Ejercicios {
// Aquí escribiremos nuestros métodos.
}
Nuestra clase Main será así:
public class Main {
public static void main(String[] args) {
// Aquí los vamos a llamar.
}
}
Ahora que la tenemos creada vamos a ir ejemplo a ejemplo escribiendo los métodos dentro de ella y posteriormente llamándolos desde dentro del método Main de la clase Main.
– En el primer ejemplo vamos a crear un método en la clase Ejercicios que nos diga si un número es par o no lo es. El número lo introduciremos manualmente cuando llamemos al método desde la clase Main.
Este es el código de nuestro método:
public class Ejercicios {
public static boolean esPar(int numero) {
if (numero % 2 == 0) {
System.out.println("El número es par");
} else {
System.out.println("El número es impar");
}
// Retorna true si el número es par, false si es impar
return numero % 2 == 0;
}
}
Hasta aquí no hay mucha pérdida, el ejercicio es sencillo. En lo que debes fijarte es en qué clase está y en el que dentro de ella hemos creado un método public static boolean. Ahora debemos llamarlo desde la clase Main.
Para ello seguimos siempre una estructura muy sencilla. Dentro de nuestra clase Main y dentro del método main escribimos el nombre de nuestra clase, después un punto y todo ello seguido del nombre del método y unos paréntesis. Si durante la declaración del método no hemos utilizado ninguna variable en los paréntesis, cuando llamemos al método desde el método main, los paréntesis estarán vacíos. Si durante la declaración utilizamos alguna variable, en los paréntesis pondremos el dato, o datos, que necesitemos. En este caso quedaría de la siguiente manera:
Ejercicios.esPar(6);
El código entero del método Main es el siguiente:
public class Main {
public static void main(String[] args) {
Ejercicios.esPar(6);
}
}
– Ahora vamos a hacer el mismo ejercicio, pero en este caso tenemos que introducir por teclado el número, después el programa nos indicará si es par o no lo es.
En la clase Ejercicios es donde hacemos todo el trabajo. Así es cómo quedaría:
import java.util.Scanner;
public class Ejercicios {
public static void esPar() {
Scanner ejercicio = new Scanner(System.in);
System.out.println("Introduce un número:");
int numero = ejercicio.nextInt();
if (numero % 2 == 0) {
System.out.println("El número es par");
} else {
System.out.println("El número es impar");
}
}
}
Vemos que el Scanner lo importamos en esta clase y no en la clase Main. Por lo demás es un código muy sencillito.
Para llamar al método desde la clase Main lo haremos de la siguiente forma:
public class Main {
public static void main(String[] args) {
Ejercicios.esPar();
}
}
– Para acabar utilizamos otro método void de tipo String. El mismo que utilizamos al principio del tema.
La clase ejercicios será la siguiente:
public class Ejercicios {
public static void texto() {
System.out.println("A veces un texto puede ser demasiado largo como para que estemos utilizándolo una y otra vez");
System.out.println("Es mejor crear un método y después llamarlo cada vez que necesitemos que se muestre dicho texto.");
System.out.println("Tu programa quedará mucho más limpio y ordenado");
}
}
Y esta será la clase Main:
public class Main {
public static void main(String[] args) {
Ejercicios.texto();
}
}
Diferencia entre clase e instancia
Quiero explicarte qué es eso de static que aparece todo el tiempo en nuestros métodos, pero para ello antes debes entender la diferencia entre clase e instancia. Una clase, como ya habíamos comentado, es un molde o plantilla que define las propiedades (atributos) y comportamientos (métodos) comunes a todos los objetos que se crearán a partir de ella.
Una instancia es un objeto creado a partir de una clase que tiene valores específicos para los atributos definidos en la clase.
Diferencia entre métodos estáticos y dinámicos
Los métodos estáticos (Todos los que hemos estado utilizando hasta ahora) están asociados directamente a la clase, no a objetos específicos de esa clase. Esto significa que se pueden invocar sin necesidad de crear una instancia (objeto) de la clase.
Por ejemplo:
public class Calculadora {
public static int sumar(int a, int b) {
return a + b;
}
}
// Para usar el método sumar, no necesitas crear un objeto. Podríamos llamarlo desde la clase Main así:
int resultado = Calculadora.sumar(5, 10);
Los métodos dinámicos pertenecen a instancias específicas de una clase. Cada objeto creado a partir de una clase tiene su propia copia de los métodos dinámicos.
public class Coche {
public void acelerar() {
System.out.println("El coche está acelerando.");
}
}
// Para usar el método acelerar, necesitas crear una instancia (objeto) Coche miCoche = new Coche();
miCoche.acelerar();
El concepto de objeto aún es algo desconocido para ti, pero vamos a poner remedio a ello. Sé que después de todo este tiempo programando de forma lineal, la utilización de métodos ha cambiado un poco tus esquemas y ahora, encima, se nos juntan las instancias. Pero la verdad es que es mucho más fácil de lo que parece. En lo que queda del tema, vamos a meternos de lleno en ello y pronto no te quedará ninguna duda al respecto.
Trabajando con instancias u objetos
¿Qué es un Objeto?
Un objeto es una entidad concreta que podemos usar en nuestros programas. Imagina que tienes un plano para construir una casa. Este plano sería la clase, que describe cómo será la casa, pero la casa real que construyes a partir del plano es el objeto. En programación, un objeto es una instancia de una clase y contiene atributos (características) y métodos (acciones).
Clases vs Objetos
- Clase: Es como un plano o receta. Describe qué características y comportamientos tendrán los objetos que se creen a partir de ella.
- Objeto: Es una instancia concreta creada a partir de la clase. Es la «cosa» real que podemos usar y manipular en nuestro programa.
¿Qué es un Constructor en Java?
Un constructor es un tipo especial de método que se usa para inicializar objetos. Es el primer método que se llama cuando creamos un objeto de una clase. Su propósito es dar valores iniciales a los atributos del objeto. Se crea dentro de la clase a la que pertenece ese objeto. Lo vamos a ver de forma mucho más clara en el ejemplo que te muestro en el siguiente punto.
Ejemplo Básico de una Clase y Objeto
- Creamos la clase: public class Persona {
public class Persona {
String nombre;
int edad;
// Constructor
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
// Método para mostrar la información de la persona
public void mostrarInformacion() {
System.out.println("Nombre: " + nombre + ", Edad: " + edad);
}
}
- Ahora vamos con el objeto
public class Main {
public static void main(String[] args) {
// Crear un objeto de la clase Persona
Persona persona1 = new Persona("Juan", 25);
// Usar el objeto para llamar al método mostrarInformacion
persona1.mostrarInformacion();
}
}
Cómo puedes ver, en la clase persona tenemos dos métodos. El primero es el constructor. Gracias a él, después en la clase Main, vamos a poder crear objetos y asignarles valores. El siguiente método simplemente va a mostrar los valores por pantalla. Utiliza la información que nos proporciona el método constructor.
En la clase Main, creamos un objeto, que en este caso llamamos Persona1, y le asignamos los valores nombre y edad. Para este caso concreto, el nombre es Juan y la edad es 25 años. Una vez que nuestro objeto ha sido creado y tiene valores asignados, utilizamos el segundo método para mostrar la información por pantalla.
Definición de Métodos Getter
Propósito: Un método getter se usa para obtener el valor de un atributo privado de una clase. Es una forma segura de acceder a los datos sin permitir que se modifiquen directamente.
public class Persona {
private String nombre;
// Método getter para el atributo nombre
public String getNombre() {
return nombre;
}
}
Definición de Métodos Setter
Propósito: Un método setter se usa para establecer o modificar el valor de un atributo privado de una clase. Permite controlar cómo se cambia el valor y puede incluir validaciones.
Sintaxis:
public class Persona {
private String nombre;
// Método setter para el atributo nombre
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
Ejemplo de Getters y Setters
Veamos un ejemplo completo con una clase Persona que tiene un atributo edad y usa métodos getter y setter para manejarlo.
public class Persona {
private int edad;
// Método getter para el atributo edad
public int getEdad() {
return edad;
}
// Método setter para el atributo edad
public void setEdad(int edad) {
if (edad > 0) {
this.edad = edad;
}
}
}
public class Main {
public static void main(String[] args) {
Persona persona1 = new Persona();
// Establecer la edad usando el setter
persona1.setEdad(25);
// Obtener la edad usando el getter
System.out.println("Edad: " + persona1.getEdad());
}
}
Esta vez, en la clase persona no hemos creado ningún constructor. Por lo tanto, si creáramos un objeto en la clase Main, nos sería imposible asignarles valor a sus atributos.
Pero no hay problema; en vez de un método constructor, utilizaremos un método Getter y uno Setter. Lo vamos a crear en la clase Persona y después lo llamaremos desde Main. En este caso, solo tenemos una variable, así que se crea un Getter y un Setter por cada variable. Ambos siguen siempre la misma estructura que puedes apreciar en el ejemplo.
En la clase Main, lo primero que hacemos es crear un objeto, de nuevo Persona1. Para llamar al Setter, ponemos el nombre de nuestro objeto, un punto, el nombre de nuestro Setter y entre paréntesis el valor que queremos asignar. Como puedes observar, para llamar al Getter, seguimos exactamente la misma estructura.