1. Clase abstracta: repasemos lo básico
Antes de pasar a la comparación, recordemos qué es una clase abstracta.
Una clase abstracta — es una clase que no puede crearse directamente (no se puede escribir new Animal()), pero puede contener tanto métodos normales (con implementación) como métodos abstractos (sin implementación). Una clase abstracta se usa a menudo como base para otras clases que heredan su comportamiento y/o están obligadas a implementar algunos métodos.
Por ejemplo, si en nuestra aplicación hay diferentes tipos de transporte, podemos crear la clase abstracta Transport:
public abstract class Transport {
private String model;
public Transport(String model) {
this.model = model;
}
public String getModel() {
return model;
}
// Método abstracto — no hay implementación, solo declaración
public abstract void move();
// Método normal — hay implementación
public void printInfo() {
System.out.println("Modelo de transporte: " + model);
}
}
Características de la clase abstracta:
- Puede contener campos (estado).
- Puede contener métodos implementados.
- Puede contener métodos abstractos (obligatorios de implementar en las subclases).
- No se puede crear una instancia directamente.
- Se utiliza para comportamiento y estado comunes.
2. Interfaz: repasemos lo básico
Una interfaz es un conjunto de métodos que una clase debe implementar. Una interfaz no describe estado (no puede tener campos normales, solo constantes) y no contiene implementaciones de métodos (hasta Java 8). Una interfaz es un contrato puro: «Si me implementas, estás obligado a saber hacer esto».
Ejemplo de interfaz:
public interface Movable {
void move(int x, int y);
}
Características de la interfaz:
- No contiene estado (solo constantes public static final).
- Hasta Java 8 — solo métodos abstractos (desde Java 8 aparecieron métodos default y static, pero hablaremos de ellos más adelante).
- Los métodos son siempre public abstract por defecto.
- Una clase puede implementar varias interfaces.
- Se utiliza para describir capacidades, «qué sabe hacer» la clase.
3. Tabla comparativa: clase abstracta vs interfaz
¡Es hora de compararlas cara a cara! Aquí tienes una tabla ilustrativa:
| Característica | Clase abstracta | Interfaz |
|---|---|---|
| Sintaxis | |
|
| ¿Se puede crear una instancia? | No | No |
| ¿Puede contener métodos normales? | Sí | Hasta Java 8 — no; desde Java 8 — solo default/static |
| ¿Puede contener métodos abstractos? | Sí | Sí (todos los métodos hasta Java 8 son abstractos) |
| ¿Puede contener campos (estado)? | Sí (cualquier campo) | Solo public static final (constantes) |
| ¿Puede contener constructores? | Sí | No |
| Herencia | Solo una clase (abstracta o normal) | Se pueden implementar varias interfaces |
| Modificadores de métodos | Cualesquiera (public, protected, private) | Los métodos por defecto son public abstract. Desde Java 9 se pueden añadir métodos private para uso dentro de la interfaz |
| Herencia mediante | |
|
| Uso habitual | Implementación y estado comunes | Descripción de capacidades y roles |
| Ejemplos del JDK | |
|
4. ¿Cuándo usar una interfaz y cuándo una clase abstracta?
Usa una interfaz cuando:
- Quieres describir «qué sabe hacer» la clase sin preocuparte de cómo lo hace.
- Necesitas que la clase pueda implementar varias capacidades independientes.
- Ejemplo: Comparable (se puede comparar), Serializable (se puede serializar), Runnable (se puede ejecutar en un hilo).
Usa una clase abstracta cuando:
- Quieres definir una implementación y un estado comunes que tendrán todas las subclases.
- Necesitas que todas las subclases tengan ciertos campos o métodos con implementación.
- La herencia es estrictamente «uno a uno»: una clase solo puede heredar de una única clase (normal o abstracta).
Analogías de la vida real
- Interfaz — es como el «permiso de conducir»: si lo tienes, puedes conducir un coche, pero nadie dice en qué coche exactamente ni cómo lo haces.
- Clase abstracta — es como el «plano general de un automóvil»: todos los coches tienen volante, pedales, motor, pero cada marca implementa los detalles a su manera.
5. Ejemplos de la biblioteca estándar de Java
Interfaz: Comparable
public interface Comparable<T> {
int compareTo(T o);
}
Cualquier clase que implemente esta interfaz está obligada a implementar el método compareTo. Por ejemplo, String, Integer, LocalDate y muchas otras.
Clase abstracta: AbstractList
public abstract class AbstractList<E> implements List<E> {
// Implementación por defecto de algunos métodos de List
// Algunos métodos se dejan abstractos
}
AbstractList ya implementa parte del comportamiento de las colecciones (por ejemplo, métodos de añadir/eliminar), pero deja algunos métodos como abstractos para que las subclases puedan implementarlos a su manera.
6. Ejemplos de código: comparación en la práctica
Interfaz
Creemos una interfaz y una clase que la implemente.
public interface Printable {
void print();
}
public class Document implements Printable {
@Override
public void print() {
System.out.println("Imprimiendo documento...");
}
}
Clase abstracta
Ahora, una clase abstracta y su subclase.
public abstract class Machine {
public void turnOn() {
System.out.println("La máquina está encendida.");
}
public abstract void work();
}
public class Printer extends Machine {
@Override
public void work() {
System.out.println("La impresora está imprimiendo...");
}
}
Una clase que usa ambos: interfaz y clase abstracta
public class SmartPrinter extends Machine implements Printable {
@Override
public void work() {
System.out.println("La impresora inteligente está funcionando...");
}
@Override
public void print() {
System.out.println("La impresora inteligente está imprimiendo...");
}
}
7. Implementación múltiple de interfaces: por qué es genial
En Java una clase solo puede heredar de una única clase (abstracta o normal), ¡pero puede implementar tantas interfaces como sea necesario! Esto permite crear arquitecturas flexibles y extensibles.
public interface Scannable {
void scan();
}
public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
@Override
public void work() {
System.out.println("El multifunción está funcionando...");
}
@Override
public void print() {
System.out.println("El multifunción está imprimiendo...");
}
@Override
public void scan() {
System.out.println("El multifunción está escaneando...");
}
}
¿Cuándo elegir qué?
- Si estás diseñando funcionalidad base con estado común (por ejemplo, campos), usa una clase abstracta.
- Si quieres añadir a los objetos «etiquetas de capacidades» (por ejemplo, «puede imprimir», «puede compararse», «puede serializarse»), usa interfaces.
- Si no estás seguro, empieza con una interfaz. En Java se considera buena práctica: las interfaces dan mayor flexibilidad y extensibilidad.
8. Errores típicos y trampas
Error n.º 1: Intentas heredar de varias clases — ¡Java no te dejará!
Una clase puede heredar solo de una clase, pero implementar muchas interfaces. Por ejemplo, class A extends B, C — error, mientras que class A extends B implements X, Y, Z — sin problema.
Error n.º 2: Confundes campos de una interfaz y de una clase.
En una interfaz solo se pueden declarar constantes (public static final). No se puede declarar estado normal, por ejemplo, private int count; — el compilador te detendrá al instante.
Error n.º 3: No implementaste todos los métodos de la interfaz.
Si una clase no implementa al menos un método de la interfaz, debe declararse como abstract; de lo contrario, el compilador generará un error.
Error n.º 4: Intentas crear una instancia de una interfaz o de una clase abstracta.
Ambos tipos son «semielaborados». Solo se pueden extender, no instanciar directamente:
Printable p = new Printable(); // ¡Error!
Machine m = new Machine(); // ¡Error!
Error n.º 5: Piensas que una interfaz puede tener constructor.
Las interfaces no pueden tener constructores, ya que no describen el estado de los objetos. Solo las clases (normales y abstractas) pueden tenerlos.
GO TO FULL VERSION