Clase abstracta
Una clase abstracta es una clase que no se puede crear directamente (no se puede hacer new de ella), sino que sirve como una plantilla o modelo para que otras clases la hereden. Se utiliza cuando queremos definir una estructura común (atributos y métodos) que compartirán varias clases, pero dejando algunos métodos sin definir porque cada clase hija los implementará a su manera. Es como un molde incompleto: marca las bases, pero deja huecos que deben rellenarse.
Las clases abstractas se usan, por ejemplo, cuando tenemos varias clases con comportamientos parecidos pero con diferencias en algunos detalles. Imagina una clase abstracta Animal con un método abstracto hacerSonido(). No tiene sentido crear un "Animal" genérico, pero sí que podemos tener clases hijas como Perro y Gato que extienden de Animal y definen su propio sonido. Así, Perro implementará hacerSonido() diciendo "guau" y Gato lo hará diciendo "miau". Esto permite organizar el código de forma más clara, evitando duplicaciones y dejando que cada clase concreta defina su propio comportamiento.
Métodos abstractos
En la jerarquía de herencia de clases, cuanto más abajo, más específica, más particular es la implementación de los métodos. Así mismo, cuanto más arriba, más general y abstracta. Hay clases que definen métodos pero que no pueden implementarlos, y delegan esto a sus subclases. Este tipo de métodos se conocen como métodos abstractos.
Para declarar un método abstracto se antepone el modificador abstract y se declara su prototipo sin escribir el cuerpo de la función:
abstract void metodo();
Las subclases deberán implementar estos métodos con sus particularidades específicas.
Clases abstractas
Toda clase que tiene un método abstracto debe ser declarada a su vez como abstract. Las clases abstractas no son instanciables. Es decir, no se pueden crear objetos de esa clase con el operador new.
Las clases abstractas existen para ser heredadas por otras y no para ser instanciadas.
Las clases abstractas pueden tener métodos implementados y atributos definidos que también se heredan.
Aunque una clase abstracta no se puede instanciar directamente, puede tener un constructor. Este constructor puede ser llamado desde el constructor de la subclase mediante super().
public abstract class A {
private int a = 1;
public void metodo1() {
System.out.println("metodo definido e implementado en A");
}
abstract void metodo2(); // metodo abstracto
}
Ahora vamos a heredar dos clases de esta superclase:
public class B extends A {
public void metodo2() {
System.out.println("metodo2 en B");
}
}
public class C extends A {
public void metodo2() {
System.out.println("metodo2 en C");
}
}
Realizamos el programa principal:
B b = new B();
C c = new C();
b.metodo1(); // Salida: metodo definido e implementado en A
b.metodo2(); // Salida: metodo2 en B
c.metodo1(); // Salida: metodo definido e implementado en A
c.metodo2(); // Salida: metodo2 en C
Ejemplo
Un uso real muy común de las clases abstractas aparece en aplicaciones que gestionan pagos.
Por ejemplo, imagina una aplicación de comercio electrónico donde los clientes pueden pagar con tarjeta, PayPal o transferencia bancaria. Todos los métodos de pago comparten cosas en común (como el importe, el destinatario o el proceso de validación), pero cada uno tiene formas diferentes de procesar el pago.
Podemos crear una clase abstracta Pago que defina lo común:
abstract class Pago {
double cantidad;
Pago(double cantidad) {
this.cantidad = cantidad;
}
void validarMonto() {
if (cantidad <= 0) {
throw new IllegalArgumentException("El monto debe ser mayor que cero.");
}
}
abstract void procesarPago(); // cada tipo de pago lo implementará distinto
}
Y luego, cada forma de pago hereda de Pago y define su propio comportamiento:
class PagoTarjeta extends Pago {
PagoTarjeta(double cantidad) {
super(cantidad);
}
@Override
void procesarPago() {
System.out.println("Procesando pago con tarjeta por " + cantidad + "€");
}
}
class PagoPayPal extends Pago {
PagoPayPal(double cantidad) {
super(cantidad);
}
@Override
void procesarPago() {
System.out.println("Procesando pago con PayPal por " + cantidad + "€");
}
}
public class Main {
public static void main(String[] args) {
// Creamos distintos tipos de pago
Pago pago1 = new PagoTarjeta(50.0);
Pago pago2 = new PagoPayPal(75.5);
// Los validamos y procesamos de forma genérica
pago1.validarMonto();
pago1.procesarPago();
pago2.validarMonto();
pago2.procesarPago();
// También podríamos guardarlos en un array o lista
Pago[] pagos = { pago1, pago2 };
System.out.println("\n--- Procesando todos los pagos ---");
for (Pago p : pagos) {
p.procesarPago(); // Llama al método correspondiente según el tipo
}
}
}
Así, el programa puede manejar distintos tipos de pagos de forma genérica (como una lista de Pago), sin importar el tipo concreto. Esto facilita ampliar la aplicación más adelante (por ejemplo, añadiendo PagoCriptomoneda) sin tener que modificar el resto del código.
Usos reales de clases abstractas
- Modelos base en aplicaciones empresariales:
- Clase abstracta
EntidadBasecon campos comunes comoid,fechaCreacion,fechaActualizacion. - Clases hijas:
Cliente,Pedido,Producto.
- Clase abstracta
- Procesamiento de pagos:
- Clase abstracta
Pagocon métodos comunes (validarMonto(),procesarPago()). - Clases hijas:
PagoTarjeta,PagoPayPal,PagoTransferencia,PagoBizum,PagoCripto.
- Clase abstracta
- Renderizado o dibujo en aplicaciones gráficas:
- Clase abstracta
Figuracon métodosdibujar()ycalcularArea(). - Clases hijas:
Circulo,Rectangulo,Triangulo.
- Clase abstracta
- Vehículos o transporte:
- Clase abstracta
Vehiculocon métodosarrancar()ydetener(). - Clases hijas:
Coche,Moto,Camion.
- Clase abstracta
- Inteligencia artificial o procesamiento de datos:
- Clase abstracta
ModeloIAcon métodosentrenar()ypredecir(). - Clases hijas:
RedNeuronal,ArbolDeDecision,RegresionLineal.
- Clase abstracta
- Acceso a bases de datos:
- Clase abstracta
Repositorio<T>con operaciones genéricas (guardar(),buscarPorId()). - Clases hijas:
RepositorioUsuario,RepositorioProducto, etc.
- Clase abstracta
- Notificaciones y mensajería:
- Clase abstracta
Notificacioncon métodosenviar()yprepararMensaje(). - Clases hijas:
NotificacionEmail,NotificacionSMS,NotificacionPush.
- Clase abstracta
- Autenticación y seguridad:
- Clase abstracta
Autenticadorcon métodoautenticar(usuario, contraseña). - Clases hijas:
AutenticadorLocal,AutenticadorEmail,AutenticadorOAuth,AutenticadorLDAP.
- Clase abstracta