Curso Java POO - Clases Abstractas
¡Buenas a todos!
Volvemos con una clase de Java y programación orientada a objetos. Cómo ha ido entrando gente nueva y me han preguntado, dejo por aquí todas las clases para que podáis seguir el curso:
¿Cómo vais con vuestro aprendizaje en Java? Espero que lo que fui explicando os sea de ayuda y sobre todo practiquéis que es como mejor se aprende. Hoy dejaré algunos ejercicios sobre lo que llevamos visto.
En la clase de hoy vamos a ver las clases abstractas. Otra parte de Java que es difícil de entender para los principiantes.
No os voy a engañar, estas clases no son muy comunes en los proyectos que os vaís a encontrar en las empresas. Lo bueno es que os van a ayudar a entender las interfaces, las vamos a ver en la clase siguiente y si que son muy usadas.
¿Qué es una clase abstracta y por qué debería importarte?
En el mundo real, hay comportamientos comunes que comparten varias entidades. Piensa en nuestra clase Cliente. Imagina que en tu aplicación tienes clientes particulares y clientes empresa.
Ambos tienen nombre, dirección y mostrarInfo(), pero la forma en que gestionan sus cuentas “gestionarCuenta()” es diferente.
Si creas dos clases separadas (ClienteParticular y ClienteEmpresa) sin una base común, pronto te verás copiando y pegando código como si fueras un mono aporreando el teclado. Mal asunto.
Aquí es donde entran en juego las clases abstractas: son plantillas que te obligan a estructurar el código de forma ordenada. Te permiten definir qué es común y qué debe ser implementado por cada tipo de cliente.
Ejemplo de clase abstracta
Aquí vamos a mezclar conceptos ya vistos en otros artículos. Mantén la mente despierta y analiza cada línea de código y cada clase para que la entiendas bien.
Trabajaremos con la clase Main en vez de usar varios archivos para hacerlo más sencillo.
Todo el código que te mostraré lo puedes pegar en el archivo Main y ejecutarlo. Recuerda que esto no es lo correcto en una aplicación real donde todas las clases se separan en archivos diferentes como ya vimos. Pero para este ejemplo nos vale.
Te dejo libertad, si te sientes seguro para que lo separes tu e importes después las clases 😜
Clase Cliente
// Clase Abstracta
abstract class Cliente {
protected String nombre;
protected String direccion;
public Cliente(String nombre, String direccion) {
this.nombre = nombre;
this.direccion = direccion;
}
public void mostrarInfo() {
System.out.println("Nombre: " + nombre);
System.out.println("Dirección: " + direccion);
}
// Método abstracto: cada tipo de cliente gestiona su cuenta de forma diferente
abstract void gestionarCuenta();
}
Análisis de lo que vemos:
Ves que añadimos “
abstract”
justo antes de la clase para hacerla abstracta.Tiene 2 atributos, nombre y dirección.
Tiene un constructor parametrizado que usa los 2 atributos anteriores.
Tiene un método “
mostrarInfo()”
que imprime en consola la información que contengan los atributos una vez instanciemos un objeto Cliente.Tiene un método abstracto “
abstract void gestionarCuenta();”.
Esto es lo importante. Este método verás que empieza por “abstract”, es “void” porque no devuelve nada (no tiene return) y tiene el cuerpo vacio. No tiene nada en su interior porque las siguientes clases que vamos a crear lo modificarán a su conveniencia.
Clase ClienteParticular
// Clase Hija: Cliente Particular
class ClienteParticular extends Cliente {
public ClienteParticular(String nombre, String direccion) {
super(nombre, direccion);
}
@Override
void gestionarCuenta() {
System.out.println("Gestionando cuenta como Cliente Particular.");
}
}
Análisis de lo que vemos:
La clase ClienteParticular “extends” de Cliente. Eso significa que va a heredar los métodos y atributos.
El constructor usa super para heredar el constructor de la clase padre Cliente, ya que los constructores no se heredan de forma automática.
Usamos “@Override” para implementar el método abstracto gestionarCuenta() de la clase padre. Este método al ser abstracto es obligatorio de implementar por todas las clases hijo.
Clase ClienteEmpresa
// Clase Hija: Cliente Empresa
class ClienteEmpresa extends Cliente {
private String cif;
public ClienteEmpresa(String nombre, String direccion, String cif) {
super(nombre, direccion);
this.cif = cif;
}
@Override
void gestionarCuenta() {
System.out.println("Gestionando cuenta empresarial con CIF: " + cif);
}
}
Análisis de lo que vemos:
En este caso, la clase tiene un atributo más que es el cif.
Se incluye en el constructor heredado.
Se utiliza en el método que implementa de gestionarCuenta().
Clase principal Main
Os la dejo como imagen para que se vea más claro.
Básicamente instanciamos un objeto clienteParticular y uno clienteEmpresa. Usamos sus constructores y método mostrarInfo() heredados de Cliente y su método abstracto gestionarCuenta() que hemos tenido que implementar en las 2 clases.
// Clase Principal public class Main { public static void main(String[] args) { ClienteParticular clienteParticular = new ClienteParticular("Juan Pérez", "Calle Falsa 123"); clienteParticular.mostrarInfo(); clienteParticular.gestionarCuenta(); System.out.println("----------------------"); ClienteEmpresa clienteEmpresa = new ClienteEmpresa("Tech Solutions S.L.", "Avenida Principal 456", "B12345678"); clienteEmpresa.mostrarInfo(); clienteEmpresa.gestionarCuenta(); } }
Salida del programa:
Resumen de la clase de hoy:
Las clases abstractas sirven como plantillas para definir comportamientos comunes sin implementar todos los detalles. En el ejercicio anterior, la clase “Cliente“ es abstracta porque hay distintos tipos de clientes, pero todos comparten atributos como “nombre” y “direccion”.
Ventajas en nuestro código:
Evitamos código duplicado →
Cliente
define lo común, yClienteParticular
yClienteEmpresa
solo implementan sus diferencias.Uso de
super(...)
→ Las subclases llaman al constructor deCliente
para reutilizar la inicialización.Métodos abstractos (
gestionarCuenta()
) → Obligan a cada subclase a definir su propia lógica.@Override
→ Garantiza que la implementación en las subclases es correcta.
Ejercicio para practicar, gestión de empleados:
Os dejo este ejercicio para ir practicando, como vereis es muy parecido a nuestro ejemplo. ¡Suerte!
En una empresa hay diferentes tipos de empleados, pero todos tienen ciertas características en común: nombre, salario y un método para calcular su bono anual.
Objetivo:
Crea una clase abstracta Empleado
con los atributos comunes y un método abstracto calcularBono()
, que cada tipo de empleado deberá implementar de manera diferente.
Requisitos:
Crea la clase abstracta Empleado
con:
Atributos
nombre
(String) ysalario
(double).Un constructor para inicializar estos atributos.
Un método
mostrarInfo()
que imprima el nombre y salario del empleado.Un método abstracto
calcularBono()
, que será diferente según el tipo de empleado.
Crea dos subclases de Empleado
:
EmpleadoTiempoCompleto
: el bono es el 10% del salario.EmpleadoFreelancer
: el bono es un monto fijo de 500 euros.
Crea una clase Empresa
con un main()
que:
Cree un empleado de tiempo completo y uno freelancer.
Muestre su información y su bono anual llamando a
calcularBono()
.