La herencia es una de las características de la POO (programación orientada a objetos), pero no es exclusiva de los lenguajes de programación orientados a objetos. Consiste en que una clase recibe los atributos y métodos de otra clase en este sentido se tiene:
- Clase base, clase padre o superclase que "hereda" sus atributos y métodos a otra clase.
- Clase derivada, clase hija o subclase que "hereda" sus atributos y métodos de otra clase.
Los objetivos de emplear la herencia en la programación es la reutilización de código (
clic aquí para ver un ejemplo de reutilización de código en un caso práctico), especialización de clases (principio abierto/cerrado) y aprovechar las ventajas el polimorfismo (característica más importante en los lenguajes OO), de esta manera se aumenta la productividad en la etapa de desarrollo produciendo programas con alta fidelidad y eficiencia. Además, se reduce el esfuerzo necesario para el mantenimiento del programa luego de su implementación.
Herencia simple
La herencia simple es aceptada por Java y se produce cuando una o más clases heredan métodos y atributos de otra única clase. Es decir existe una sola clase base y una o más clases derivadas. La palabra reservada en el lenguaje java para indicar herencia es "extends".
Consideraciones
- En el momento de programar, nos daremos con la sorpresa que los constructores no se heredan y para hacer uso de ellos se debe emplear el método "super" en el constructor de la clase derivada.
- Si aplicamos herencia, es una buena práctica hacer un llamado al constructor de la clase base (método "super")
- La clase derivada hereda todos los métodos de la clase base, pero no puede acceder directamente a los atributos, pues tienen un nivel acceso "privado".
- Para que los métodos de una superclase sean accesibles por una subclase se deben modificar sus niveles de acceso a "protegido" o "público".
- Se pueden refinar los métodos en las clases hijas si el método tiene el mismo nombre en la clase hija y la clase padre.
- Se puede llamar dentro de un método de la clase derivada el de la clase base mediante "super"
- El modificados "final" en la clase base tiene diversos comportamientos según donde sea aplicado
- En una variable, la convierte en una constante
- Ejemplo: protected final String NOMBRE = "Jhon";
- En un método, evita que pueda ser redefinido en la clase derivada.
- En la clase (Ejemplo: public final class Autor {/* ... */}) evita que la clase pueda ser una clase padre, sería como la última clase hija en la jerarquía de clases.
Ejemplo
Supongamos una clase "Vehiculos" y una clase "Automoviles", donde "Automoviles" va heredar de la clase "Vehiculos":
Código de "Vehiculos"
package dto;
public class Vehiculos {
private String nombre;
private Double costo;
private String marca;
public Vehiculos(String nombre) {
this.nombre = nombre;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public Double getCosto() {
return costo;
}
public void setCosto(Double costo) {
this.costo = costo;
}
public String getMarca() {
return marca;
}
public void setMarca(String marca) {
this.marca = marca;
}
}
Código de "Automoviles"
package dto;
public class Automoviles extends Vehiculos {
private String modelo;
private Float cilindrada;
public Automoviles() {
super("Automovil");
}
public String getModelo() {
return modelo;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public Float getCilindrada() {
return cilindrada;
}
public void setCilindrada(Float cilindrada) {
this.cilindrada = cilindrada;
}
@Override
public String toString() {
return String.format(
"Tipo de vehículo: %s\nMarca: %s\nModelo: %s\nCilindrada: %.2f\nCosto: $%.2f",
getNombre(),getMarca(), modelo, cilindrada, getCosto());
}
}
Ejecución
package test;
import dto.Automoviles;
public class Ejecutor {
public static void main(String[] args) {
Automoviles a = new Automoviles();
a.setModelo("Elantra");
a.setMarca("Hyundai");
a.setCilindrada(16.4f);
a.setCosto(25000d);
System.out.println(a.toString());
}
}
Estructura de los archivos:
Salida:
Tipo de vehículo: Automovil
Marca: Hyundai
Modelo: Elantra
Cilindrada: 16.40
Costo: $25000.00
Herencia múltiple
La herencia múltiple se da cuando una clase hija puede tener más de una clase padre, esto no es soportado de manera nativa por Java debido al problema del diamante y consideraciones que los desarrolladores del lenguaje, pues causaba más problemas que soluciones su uso. Por ello, para poder aplicar una herencia múltiple se considera dos tipos: a) herencia múltiple de implementación de clases (usando extends) y b) la herencia múltiple con implementación de tipos (empleando interfaces). La primera no es soportada por Java, pero la segunda sí mediante el uso de interfaces y la palabra reservada "implements", así ingresamos a la vez al mundo del polimorfismo (otra de las características de la POO y principal ventaja de los lenguajes de este tipo frente a otros por su facilidad de implementar).
Consideremos el siguiente caso donde la clase "Automoviles" debe heredar tanto de "Vehiculos" como de la clase "Activos". Podemos considerar crear una interfaz llamada "Activos" donde definimos los atributos que serían simplemente constantes y los métodos simplemente declarados, pero no implementados.