Herencia, Clases Abstractas y Jerarquías Sealed
Una subclase hereda campos y métodos de su superclase con extends (solo de una). Una clase abstract no se puede instanciar y puede declarar métodos sin cuerpo que las subclases deben implementar. Con sealed ... permits decides exactamente qué clases pueden heredar.
En una línea public abstract sealed class Pago permits PagoTarjeta, PagoBizum { abstract BigDecimal comision(); }
public abstract sealed class Pago permits PagoTarjeta, PagoBizum {
protected final BigDecimal importe;
protected Pago(BigDecimal importe) {
if (importe.signum() <= 0) throw new IllegalArgumentException("importe <= 0");
this.importe = importe;
}
public abstract BigDecimal comision(); // cada tipo la calcula a su manera
public BigDecimal total() { // comportamiento común heredado
return importe.add(comision());
}
}
public final class PagoTarjeta extends Pago {
public PagoTarjeta(BigDecimal importe) { super(importe); }
@Override public BigDecimal comision() { return importe.multiply(new BigDecimal("0.015")); }
}
public final class PagoBizum extends Pago {
public PagoBizum(BigDecimal importe) { super(importe); }
@Override public BigDecimal comision() { return BigDecimal.ZERO; }
}
// Composición en lugar de herencia: el servicio TIENE un notificador
public class ServicioCobros {
private final Notificador notificador;
public ServicioCobros(Notificador notificador) { this.notificador = notificador; }
}
Cómo funciona por dentro
Con extends, la subclase hereda los miembros no privados de la superclase y puede añadir o redefinir comportamiento. El constructor de la subclase llama siempre primero a un constructor de la madre (super(...)), de forma explícita o implícita. Una clase abstract sirve de plantilla: no se instancia, puede tener estado y métodos concretos, y obliga a las hijas a implementar sus métodos abstract.
El riesgo de la herencia es el acoplamiento: la hija depende de cómo está implementada la madre, no solo de lo que promete. Si la madre cambia un método del que depende la hija, la hija puede romperse sin que nadie la toque. Por eso la regla práctica es heredar solo cuando la relación es «es un» y la hija puede sustituir a la madre en cualquier sitio (principio de Liskov); en el resto de casos, composición.
Desde Java 17, sealed permite cerrar la jerarquía: sealed class Pago permits Tarjeta, Bizum dice que no hay más subclases posibles. Cada subclase permitida debe declararse final, sealed o non-sealed. A cambio, el compilador conoce todos los casos y un switch con patrones puede ser exhaustivo sin default.
class Pila extends ArrayList), exponiendo métodos que rompen las reglas de la subclase.
sealed son estándar desde Java 17. En Java 21, switch con patrones las aprovecha para comprobar que tratas todos los casos.
Se conecta con Composición frente a herenciaPrincipio de sustitución de LiskovSealed + switch
Compruébalo: ¿Compila final class PagoCripto extends Pago en otro fichero si no aparece en permits?
No. Solo las clases listadas en permits pueden heredar de una clase sealed; el compilador rechaza cualquier otra.