Menu

Clases abstractas en Java: la palabra clave abstract explicada

Qué es una clase abstracta en Java, cómo declarar métodos abstractos, por qué no puedes instanciarla y cuándo elegir una clase abstracta en lugar de una interfaz.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Qué es una clase abstracta

Una interfaz declara comportamiento sin estado. Una clase normal está completamente implementada y se puede instanciar. Una clase abstracta se sitúa entre ambas: puede llevar campos, constructores y métodos terminados como una clase normal, pero también puede dejar algunos métodos sin implementar y prohíbe ser instanciada directamente. Se marca con la palabra clave abstract.

La idea es capturar en un solo lugar todo lo que las subclases tienen en común, obligando a cada subclase a rellenar las partes que realmente difieren.

Animal define getName() una sola vez para todas las subclases y declara sound() como abstract: un método con firma pero sin cuerpo que Dog debe proporcionar.

No puedes instanciar una clase abstracta

Como una clase abstracta puede tener métodos sin terminar, crearla directamente te dejaría con un objeto incompleto. El compilador lo rechaza:

Animal a = new Animal("???");   // error: Animal is abstract; cannot be instantiated

Siempre instancias una subclase concreta, una que haya implementado todos los métodos abstractos. Esa instancia de la subclase puede luego almacenarse en una variable del tipo abstracto, que es exactamente como se usa la abstracción.

Los métodos abstractos obligan a decidir a las subclases

Un método abstract es una promesa que la subclase debe cumplir. Si una subclase olvida implementar uno, la propia subclase pasa a ser abstracta y el compilador te lo indica. Esta es la principal palanca de la clase abstracta: garantiza que cierto comportamiento existe sin dictar qué hace.

describe() se escribe una sola vez en Shape y aun así llama al area() propio de cada subclase. La clase abstracta aporta el andamiaje común; las subclases aportan los detalles.

Estado compartido y constructores

A diferencia de una interfaz tradicional, una clase abstracta puede tener campos de instancia y definir constructores. El constructor nunca crea por sí solo un Animal o un Shape: se ejecuta mediante super(...) cuando se crea una subclase, inicializando el estado compartido.

El campo balance, deposit y applyInterest viven en un solo lugar. Solo la política que realmente varía, interestRate(), se deja abstracta. El constructor de una subclase debe llamar a super(...) para inicializar ese estado heredado.

Un detalle importante: mezclar abstract y final

abstract y final son opuestos. Un método abstract exige una sobrescritura; un método final la prohíbe. Marcar lo mismo de ambas formas, o hacer una clase abstracta final, es un error de compilación. Recuerda también que una clase abstracta puede tener cero métodos abstractos: declarar una clase abstract solo para impedir que se instancie es legal y a veces útil para tipos base que solo quieres extender.

abstract final class Bad { }        // error: abstract and final conflict

abstract class Base {
    abstract final void f();        // error: an abstract method can't be final
}

Clase abstracta frente a interfaz

Se solapan, así que la elección depende de qué necesites compartir:

  • Clase abstracta: úsala para clases estrechamente relacionadas que comparten estado y código. Tanto Savings como Checking extienden Account, heredando el campo balance y la lógica de deposit. Una clase solo extiende una.
  • Interfaz: úsala para una capacidad que clases no relacionadas pueden compartir. Un Bird y un Airplane pueden ser ambos Flyable sin compartir ninguna implementación. Una clase puede implementar muchas.

Un patrón frecuente las combina: una interfaz define el contrato y una clase abstracta implementa el código repetitivo para que las subclases concretas rellenen solo lo que es único.

A continuación: Polimorfismo

Fíjate en que en todos los ejemplos anteriores guardamos una instancia de subclase en una variable del tipo abstracto y luego, al llamar a un método, obtuvimos automáticamente el comportamiento de la subclase. Esa única capacidad —un tipo de referencia, muchos comportamientos en tiempo de ejecución— es el polimorfismo, y es lo que hace que las clases abstractas y las interfaces merezcan la pena. Eso es lo que veremos en la siguiente página.

Preguntas frecuentes

¿Qué es una clase abstracta en Java?

Una clase abstracta es una clase declarada con la palabra clave abstract que no se puede instanciar por sí sola. Está pensada para ser extendida. Puede combinar métodos y campos totalmente implementados (estado y código compartidos para las subclases) con métodos abstract que no tienen cuerpo: estos pasan a ser responsabilidad de cada subclase, que debe implementarlos.

¿Se puede instanciar una clase abstracta en Java?

No. new AbstractType() es un error de compilación porque una clase abstracta puede tener métodos sin implementar (abstract), de modo que el objeto quedaría incompleto. Lo que se instancia es una subclase concreta que implementa todos los métodos abstractos, y luego se almacena en una variable del tipo abstracto.

¿Cuál es la diferencia entre una clase abstracta y una interfaz en Java?

Una clase abstracta puede tener campos de instancia, constructores y lógica parcialmente implementada, pero una clase solo puede extender una. Una interfaz declara comportamiento sin estado de instancia, y una clase puede implementar muchas. Usa una clase abstracta para compartir estado y código entre subclases estrechamente relacionadas; usa una interfaz para dar a clases no relacionadas una capacidad común.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR