Superclases y subclases
La forma de expresar cuál es la superclase de la que heredamos es mediante la palabra reservada extends:
public class SubClase extends SuperClase {
// Código...
}
Supongamos que disponemos de la clase Persona con los atributos nombre, edad y estatura, y necesitamos construir la clase Empleado.
public class Persona {
private String nombre;
private int edad;
private double estatura;
// Resto del código...
}
Podemos definir a un empleado como una persona con un sueldo. Vamos a definir Empleado haciendo que herede de Persona, lo que hará que herede todos los atributos y métodos, y a continuación añadiremos el sueldo como atributo.
public class Empleado extends Persona {
private double sueldo;
// Resto del código...
}
El proceso de herencia puede continuar ampliándose. Por ejemplo, podemos definir a partir de Empleado la clase Jefe, que no es más que un empleado con algunas propiedades adicionales.
En Java, una clase solo puede heredar de una superclase. A esto se le conoce como herencia simple. En otros lenguajes como Python existe la herencia múltiple.
Por el contrario, una superclase no tiene límite de subclases.
Modificador de acceso en la herencia
Todos los atributos y miembros de una clase son heredados, incluso los privados, solo que estos no son visibles desde la subclase.
Por otro lado, con los tipos de datos mencionados hasta el momento, para que un atributo o método sea visible desde una subclase, podemos hacer uso de un nuevo modificador de acceso: protected. Funciona de forma muy parecida a la visibilidad por defecto, con la diferencia de que los atributos y métodos protegidos serán visibles para las clases que hereden, independientemente de si la superclase y la subclase se encuentran en el mismo paquete o en uno externo.
Resumen de visibilidades:
| La propia clase | Clases del mismo paquete | Subclases | Clases externas | |
|---|---|---|---|---|
private | ✔ | |||
| sin modificador | ✔ | ✔ | ||
protected | ✔ | ✔ | ✔ | |
public | ✔ | ✔ | ✔ | ✔ |
Clases finales
En Java, la palabra clave final puede aplicarse a clases. Cuando se aplica a una clase, final significa que esa clase no puede tener herencia, es decir, no puede utilizarse como superclase para crear otras clases derivadas.
public final class ClaseFinal {
// Métodos y atributos de la clase
}
Declarar una clase como final puede ser útil en varios casos:
- Seguridad e integridad: Usar
finalprotege la implementación de la clase para evitar que pueda ser alterada o extendida, lo que es especialmente importante en clases que forman parte de la infraestructura o de librerías críticas. Esto garantiza que la funcionalidad básica no será alterada, evitando posibles problemas de seguridad. - Optimización del rendimiento: En las clases
final, el compilador y la máquina virtual de Java pueden realizar optimizaciones que no serían posibles si la clase fuera heredable. Por ejemplo, dado que los métodos de una clasefinalno pueden sobrescribirse, pueden optimizarse para ser llamados más rápidamente. - Diseño de API o librerías: Cuando se desarrolla una API o una librería, es útil marcar algunas clases como
finalpara definir claramente cuáles permiten herencia y cuáles no. Esto permite controlar cómo otros desarrolladores pueden usar la API, evitando que extiendan clases que no están diseñadas para ser modificadas o ampliadas.
Clases finales de la API de Java
Algunas clases estándar de Java están marcadas como final debido a sus propósitos específicos y a su uso generalizado. Por ejemplo, String. Esta clase en Java es final, lo que impide que se herede. Esto asegura la inmutabilidad de las cadenas de texto en Java, que es uno de los motivos por los que String es segura y eficiente.