Herencia
En las lecciones anteriores, ha visto la herencia mencionada varias veces. En el lenguaje Java, las clases se pueden derivar de otras clases, heredando así campos y métodos de esas clases.
Excepto
Object
, que no tiene superclase, cada clase tiene una y sólo una superclase directa (herencia única). En ausencia de cualquier otra superclase explícita, cada clase es implícitamente una subclase de Object
. Las clases pueden derivarse de clases derivadas de clases derivadas de clases, y así sucesivamente, y en última instancia derivado de la clase superior,
Object
. Se dice que dicha clase desciende de todas las clases de la cadena de herencia que se remonta a Object
. La idea de herencia es simple pero poderosa: cuando desea crear una nueva clase y ya existe una clase que incluye parte del código que desea, puede derivar su nueva clase de la clase existente . Al hacer esto, puede reutilizar los campos y métodos de la clase existente sin tener que escribirlos (¡y depurarlos!) Usted mismo.
Una subclase hereda todos los miembros (campos, métodos y clases anidadas) de su superclase. Los constructores no son miembros, por lo que no son heredados por subclases, pero el constructor de la superclase puede invocarse desde la subclase.
La jerarquía de clases de la plataforma Java
La Object
, definida en el paquete java.lang
, define e implementa un comportamiento común a todas las clases, incluidas las que usted escribe. En la plataforma Java, muchas clases derivan directamente de Object
, otras clases derivan de algunas de esas clases, y así sucesivamente, formando una jerarquía de clases.
Todas las clases en la plataforma Java son descendientes de objetos
En la parte superior de la jerarquía, Object
es la más general de todas las clases. Las clases cercanas al final de la jerarquía proporcionan un comportamiento más especializado.
Un ejemplo de herencia
Aquí está el código de muestra para una posible implementación de un Bicycle
clase que se presentó en la lección Clases y objetos:
Una declaración de clase para una clase MountainBike
que es una subclase de Bicycle
podría verse así:
public class MountainBike extends Bicycle { // the MountainBike subclass adds one field public int seatHeight; // the MountainBike subclass has one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
hereda todos los campos y métodos de Bicycle
y agrega el campo seatHeight
y un método para configurarlo. Excepto por el constructor, es como si hubiera escrito una nueva clase MountainBike
completamente desde cero, con cuatro campos y cinco métodos. Sin embargo, no tuvo que hacer todo el trabajo. Esto sería especialmente valioso si los métodos de la clase Bicycle
fueran complejos y hubieran necesitado mucho tiempo para depurarlos.
Qué puede hacer en una subclase
Una subclase hereda todos los miembros públicos y protegidos de su padre, sin importar en qué paquete esté la subclase. Si la subclase está en el mismo paquete que su padre, también hereda los miembros privados del paquete del padre. Puede usar los miembros heredados tal cual, reemplazarlos, ocultarlos o complementarlos con nuevos miembros:
- Los campos heredados pueden usarse directamente, como cualquier otro campo.
- Puede declarar un campo en la subclase con el mismo nombre que el de la superclase, ocultándolo (no recomendado).
- Puede declarar nuevos campos en la subclase que no están en la superclase.
- Los métodos heredados se pueden usar directamente como están.
- Puede escribir un nuevo método de instancia en el subclase que tiene la misma firma que el de la superclase, por lo que la anula.
- Puede escribir un nuevo método estático en la subclase que tenga la misma firma que el de la superclase, ocultándolo así.
- Puede declarar nuevos métodos en la subclase que no están en la superclase.
- Puede escribir un constructor de subclase que invoque al constructor de la superclase, ya sea implícitamente o usando la palabra clave
super
.
Las siguientes secciones de esta lección ampliarán estos temas.
Miembros privados en una superclase
Una subclase no hereda los private
miembros de su clase principal. Sin embargo, si la superclase tiene métodos públicos o protegidos para acceder a sus campos privados, estos también pueden ser utilizados por la subclase.
Una clase anidada tiene acceso a todos los miembros privados de su clase adjunta, tanto a los campos como a los métodos. Por lo tanto, una clase anidada pública o protegida heredada por una subclase tiene acceso indirecto a todos los miembros privados de la superclase.
Casting Objects
Hemos visto que un objeto es del tipo tipo de datos de la clase desde la que se instanciaron. Por ejemplo, si escribimos
public MountainBike myBike = new MountainBike();
entonces myBike
es de tipo MountainBike
.
MountainBike
desciende de Bicycle
y Object
. Por lo tanto, un MountainBike
es un Bicycle
y también es un Object
, y puede ser se utiliza siempre que se soliciten objetos Bicycle
o Object
.
Lo contrario no es necesariamente cierto: un Bicycle
puede ser un MountainBike
, pero no necesariamente. Del mismo modo, un Object
puede ser un Bicycle
o MountainBike
, pero no es necesariamente.
La transmisión muestra el uso de un objeto de uno tipo en lugar de otro tipo, entre los objetos permitidos por herencia e implementaciones. Por ejemplo, si escribimos
Object obj = new MountainBike();
entonces obj
es un Object
y un MountainBike
(hasta que se asigne a obj
otro objeto que no sea MountainBike
). Esto se llama conversión implícita.
Si, por otro lado, escribimos
MountainBike myBike = obj;
lo haríamos obtiene un error en tiempo de compilación porque el compilador no sabe que obj
sea un MountainBike
. Sin embargo, podemos decirle al compilador que prometemos asignar un MountainBike
a obj
mediante conversión explícita:
MountainBike myBike = (MountainBike)obj;
Esta conversión inserta una verificación en tiempo de ejecución de que obj
tiene asignado un MountainBike
para que el compilador pueda asumir con seguridad que obj
es un MountainBike
. Si obj
no es MountainBike
en tiempo de ejecución, se lanzará una excepción.
instanceof
. Esto puede evitarle un error de ejecución debido a un lanzamiento incorrecto. Por ejemplo:
if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj;}
Aquí, el operador instanceof
verifica que obj
se refiere a un MountainBike
para que podamos realizar la conversión con el conocimiento de que no se lanzará ninguna excepción de tiempo de ejecución.