Herencia y Abstracción en Java
Aprende cómo aplicar herencia entre clases y definir estructuras abstractas reutilizables con buenas prácticas.
Introducción
La herencia y la abstracción son pilares fundamentales de la programación orientada a objetos. Te permiten escribir código más limpio, reutilizable y organizado.
¿Qué es la herencia?
La herencia permite que una clase (subclase) herede atributos y métodos de otra clase (superclase). Se define con la palabra clave extends
.
- Evita duplicación de código.
- Permite extender comportamientos comunes.
- Utiliza el principio de "general a específico".
¿Qué es una clase abstracta?
Una clase abstracta no puede instanciarse. Sirve como base para otras clases que deben implementar sus métodos abstractos.
- Se define con
abstract
. - Puedes tener métodos implementados y métodos abstractos.
- Sirve como plantilla común para otras clases.
// Clase abstracta base
public abstract class Animal {
protected String nombre;
public Animal(String nombre) {
this.nombre = nombre;
}
public void saludar() {
System.out.println("Hola, soy un animal llamado " + nombre);
}
// Método abstracto (obligatorio de implementar)
public abstract void hacerSonido();
}
// Clase concreta que hereda
public class Perro extends Animal {
public Perro(String nombre) {
super(nombre);
}
@Override
public void hacerSonido() {
System.out.println("¡Guau!");
}
}
// Otra clase concreta
public class Gato extends Animal {
public Gato(String nombre) {
super(nombre);
}
@Override
public void hacerSonido() {
System.out.println("¡Miau!");
}
}
// Uso en código
Animal perro = new Perro("Rex");
perro.saludar(); // Hola, soy un animal llamado Rex
perro.hacerSonido(); // ¡Guau!
Animal gato = new Gato("Michi");
gato.saludar(); // Hola, soy un animal llamado Michi
gato.hacerSonido(); // ¡Miau!
Buenas prácticas
- Aplica herencia solo cuando exista una relación "es un" (por ejemplo: un Perro *es un* Animal).
- Usa clases abstractas cuando tengas una lógica común que debe ser compartida.
- Marca métodos como
abstract
solo si todas las subclases deben implementarlos. - Evita herencias profundas (más de 2-3 niveles), usa composición si es necesario.
- Apóyate en interfaces si la clase no tiene lógica común.
Errores comunes
- Usar herencia solo para reutilizar código (mejor usar composición).
- Forzar herencia entre clases que no tienen relación lógica.
- Olvidar usar
super()
en el constructor de la subclase. - No implementar métodos abstractos (generará error en compilación).
Ejemplo aplicado
Supón que estás creando una app de adopción de animales. En lugar de repetir código para Perro
y Gato
, defines una clase abstracta común llamada Animal
.
List<Animal> animales = List.of(
new Perro("Toby"),
new Gato("Luna")
);
for (Animal animal : animales) {
animal.saludar();
animal.hacerSonido();
}
Nota Importante
La herencia no debe usarse como una herramienta de ahorro de código, sino como una forma de modelar relaciones reales y lógicas entre entidades.
Conclusión
Herencia y abstracción son poderosas cuando se usan bien. Te permiten diseñar sistemas extensibles, limpios y con bajo acoplamiento. En el siguiente capítulo abordaremos **interfaces** y su diferencia con clases abstractas.