Sunteți pe pagina 1din 41

7

Herencia
Conceptos fundamentales:
Comprender los aspectos bsicos de la herencia.
Invocar constructores de superclases.
Usar super para acceder a miembros de superclases.
Crear una jerarqua de clases multinivel.
Saber cundo invocar constructores.
Comprender las referencias de superclases de objetos de subclases.
Reemplazar mtodos.
Usar mtodos reemplazados para entrega dinmica de mtodos.
Usar clases abstractas.
Usar f in al.
La clase Ob j e et.
La herencia es uno de los tres principios fundamentales de la programacin orien-
tada a objetos ya que permite crear clasificaciones jerrquicas. Por medio de la herencia,
puede crear una clase general que defina rasgos comunes a una serie de elementos rela-
cionados. Esta clase se puede heredar por otras ms concretas y cada una puede aadir
elementos propios.
fttl
7. Herencia
En Java, una clase heredada se denomina superclase y la clase que realiza la herencia
se denomina subclase. Por tanto, una subclase es una versin especializada de una
superclase. Hereda todas las variables y mtodos definidos por la superclase y aade
sus propios elementos exclusivos.
Aspectos bsicos de la herencia
Java admite la herencia al permitir que una clase incorpore otra en su declaracin.
Para ello utiliza la palabra clave extends. Por tanto, la subclase se aade o ampla la
superclase. Comenzaremos con un breve ejemplo que ilustra algunas de las principales
caractersticas de la herencia. El siguiente programa crea la superclase TwoDShape, que
almacena la anchura y altura de un objeto de dos dimensiones, y la subclase Triangle.
La palabra extends se usa para crear una subclase.
11 Una sencil la jerarqua de clases .
11 Una clase para objetos de dos dimensiones.
c l ass TwoDShape {
double width;
double height;
void showDim ()
System.out . println ( " Width and height are " + width + " and " + height) ;
11 Una subclase de TwoDShape para tringul os .
class Triangle extends TwoDShape {
String style;
doubl e a rea ()
return width * height / 2 ; // Triangle puede hacer referencia a los miembros
// de TwoDShape como si fueran parte de Triangle.
void showStyle () {
System. out . println (" Triangle is " + style) ;
class Shapes {
public static void main(String args[))
Triangle tl new Triangle ();
Triangle t2 = new Triangle ();
tl. width = 4. O;
tl.height = 4 . 0; /! todos los miembros de Triangle estn disponibles para
// objetos Triangle, incluso los heredados de TwoDShape
tl . style = " filled";
t2 . width = 8 . 0;
t2 . height = 12 . 0;
t2 . style = " outlined";
System. out . println( " Info for tl : " ) ;
tl . showStyle() ;
tl . showDim( );
System.out . println( " Area is " + tl . area()) ;
System. out.println( );
System. out.println( " Info for t2: " ) ;
t2 . showStyle ( ) ;
t2 . showDi m( ) ;
System.out . println( " Area is "+ t2 . area()) ;
Este programa genera el siguiente resultado:
Info for tl:
Triangle i s filled
Width and height are 4 . 0 and 4 . 0
Area is 8 . 0
Info f or t2 :
Triangle is outlined
Width and height are 8 . 0 and 12 . 0
Area is 48 . 0
Java7 f.jQ
TwoDShape define los atributos de una forma bidimensional genrica, como un
cuadrado, un rectngulo, un tringulo, etc. La clase Tri angle crea un tipo especfico
de TwoDShape, en este caso un tringulo. La clase Tria ngl e incluye la totalidad de
TwoDShape y aade el campo s t yle, el mtodo a rea () y el mtodo s howStyle () . El
estilo del tringulo se almacena en s t y le, que puede ser cualquier cadena que describa
el tringulo, como por ejemplo "f i l led ", "out lined", " transparent " o incluso algo
como "war ning s ymbol ", " i s os celes" o " rounded" . El mtodo a rea () calcula y
devuelve el rea del tringulo, y showSt yle () muestra su estilo.
Como Triangle incluye todos los miembros de su superclase, TwoDShape, puede
acceder a wi dt h y height dentro de are a ().Adems, dentro demain () , los objetos tl
y t 2 pueden hacer referencia directamente a width y height, como si fueran parte de
Tr i a ng l e. En la figura 7.1 se muestra la incorporacin de TwoDShape a Triangle.
Aunque TwoDShape es una superclase de Tr i angle, tambin es una clase indepen-
diente. El ser la superclase de una subclase no significa que no se puede usar por s sola.
Por ejemplo, el siguiente cdigo es totalmente vlido:
TwoDShape shape = new TwoDShape();
shape . width = 10;
shape.height = 20;
shape . showDim( ) ;
fJ!i 7. Herencia
TwoDShape{
width
height
showDim()
style
Triangle
area()
showStyle( )
Figura 7.1. Descripcin conceptual de la clase Triangle.
Evidentemente, un objeto de TwoDShape no conoce ni puede acceder a las subclases
de TwoDShape. El formato general de una declaracin de clase que herede una super-
clase es el siguiente:
class nombre-subclase extends nombre- superclase{
//cuerpo de la clase
Slo puede especificar una superclase por subclase. Java no admite la herencia de
varias superclases en una misma subclase. (Difiere a este respecto con C++, donde
puede heredar varias clases base; no lo olvide cuando convierta cdigo de C++ a Java.
No obstante, puede crear una jerarqua de herencia en la que una subclase se convierta
en superclase de otra subclase. Evidentemente, ninguna clase puede ser superclase de
s misma. Una de las principales ventajas de la herencia es que tras crear una superclase
que defina los atributos comunes a un grupo de objetos, se puede usar para crear todas
las subclases especficas que desee. Cada subclase se ajustar a su propia clasificacin.
Por ejemplo, veamos otra subclase de TwoDShape que contiene rectngulos:
11 Una subclase de TwoDShape para rectngulos.
class Rectangle extends TwoDShape {
boolean isSquare() {
if(width == height) return true;
return false;
double area() {
return width * height;
La clase Rectangle incluye TwoDShape y aade el mtodo isSquare (), que
determina si el rectngulo es cuadrado, y el mtodo area (), que calcula el rea deI
rectngulo.
Acceso a miembros y herencia
Como vimos en el captulo anterior, una variable de instancia de una clase puede
declararse como priva te para evitar su uso no autorizado. La herencia de una clase
no anula la restriccin de acceso prvate. Por tanto, aunque una subclase incluye!
Java 7 fjllj
todos los miembros de su superclase, no puede acceder a los que se hayan declarado
como pri vate. Por ejemplo, width y height son privados en Two DShape, por lo que
Triangle no podr acceder a estos miembros:
// Los miembros privados no se heredan .
11 Este ejemplo no se compila .
// Una c l ase para objetos de dos cimensiones.
class TwoDShape {
pri vate double width; // ahora
private double height ; // son privados
void showDim ( )
System.out.println( " Wi dth and hei ght are " + widt h + " and " + height) ;
// Una s ubcl ase de TwoDShape for t ringulos .
class Triangle extends TwoDShape {
String style;
double a r e a ()
ret urn width * height / 2; // Error ! no se puede acceder
void showStyl e() {
System. out . p r int l n (" Triangle is " + style) ;
La clase Triangle no se compila ya que la referencia a width y h e i ght dentro del
mtodo area () supone una violacin de acceso. Como wi dth y heig h t se han decla-
rado como priv a te, slo son accesibles para los miembros de sus propias clases. Las
subclases no tienen acceso a ellos.
Recuerde que un miembro de clase declarado como p r iva te sigue siendo privado
para su clase. No es accesible para cdigo externo a su clase, incluidas superclases.
Puede pensar que el hecho de que las subclases no puedan acceder a los miembros
privados de una superclase es una seria limitacin que impide el uso de miembros
privados en muchos casos. Pero no es cierto. Como indicamos en el captulo anterior,
los programadores de Java suelen usar mtodos de acceso para proporcionar acceso a
los miembros privados de una clase. En la siguiente versin de las clases TwoDShape
y Triangle se usan mtodos para acceder a las variables de instancia privadas wi dth
y heigh t:
// Usar mtodos de acceso para establecer y recuperar mi embros privados .
// Una clase para objetos de dos dimensiones.
class TwoDShape {
7. Herencia
private double width; // ahora
private double height ; // son privados
11 Mtodos de acceso para width y height.
double getWidth() { return width; }
double getHeight() { return height; }
void setWidth(double w) { width = w; }
void setHeight (double h) { height = h;
void showDim() {
System.out . println( " Width and height are " + width + " and " + height);
11 Una subclase de TwoDShape para tringulos.
class Triangle extends TwoDShape {
String style;
double area ()
return getWidth() * getHeight() / 2; // usar mtodos de acceso de la superclase
void showStyle() {
System. out . println( " Triangle is"+ style) ;
class Shapes2 {
public static void main(String args[])
Triangle tl new Triangle();
Triangle t2 = new Triangle() ;
tl . setWidth ( 4 . 0);
tl . setHeight(4 . 0) ;
tl . style = " filled" ;
t2 . setWidth(8.0);
t2.setHeight(12 . 0);
t2 . style = " outlined" ;
System. out . println ( "Info for tl : " );
tl.showStyle() ;
t1 . showDim () ;
System. out . println ("Area is " + tl.area ( ));
System.out.println();
System.out . println( " Info for t2: " );
t2.showStyle();
t2.showDim();
System.out . println( "Area is " + t2.area());
Java 7 fjfl
Preguntas al experto
P: cundo debe ser privada una variable de instancia?
R: No existen reglas concretas pero s dos principios generales. Si una variable de
instancia slo va a utilizarse con mtodos definidos en su clase, debe convertirse en
privada. Si una variable de instancia debe estar comprendida entre ciertos lmites,
debe ser privada y estar nicamente disponible para los mtodos de acceso. De este
modo evitar la asignacin de valores no vlidos.
Constructores y herencia
En una jerarqua, tanto superclases corno subclases pueden tener sus propios cons-
tructores, lo que genera una duda: qu constructor se encarga de crear un objeto de la
subclase, el de la superclase o el de la subclase? La respuesta es que el constructor de la
superclase crea la parte de superclase del objeto y el constructor de la subclase crea la
parte de subclase. Tiene sentido ya que la superclase no conoce ni puede acceder a ningn
elemento de la subclase. Por tanto, su construccin debe ser independiente. Los ejem-
plos anteriores dependan de la creacin automtica de constructores predeterminados
por parte de Java, por lo que no era un problema. Sin embargo, en la prctica, muchas
clases tendrn constructores explcitos. Veremos cmo afrontar esta situacin. Cuando
slo la subclase define un constructor, el proceso es sencillo: basta con crear el objeto de
subclase. La parte de superclase del objeto se crea automticamente con su constructor
predeterminado. La siguiente versin modificada de Tr i ang l e define un constructor y
tambin convierte a style en pri vat e , ya que ahora se establece en el constructor.
// Aadir un constructor a Triangle .
// Una clase para objetos de dos dimensiones .
class TwoDShape {
private double width; // ahora
private double height ; // son privados
//Mtodos de acceso para width y he i ght .
double getWidth() { return width; }
doubl e getHeight () { return height; }
void setWidth (double w) { width = w; }
void setHeigh t(double h) { height = h ;
void showDim ( ) {
System. out . println ( " Width and height are " + width + " and " + height);
// Una subcl ase de TwoDShape para triangles .
class Triangle extends TwoDShape {
private String style;
f .lml 7. Herencia
11 Constructor
Triangle (String s , doubl e w, double h) {
setWidth (w) ;
setHeight(h); //inicializar la parte TwoDShape del objeto
style = s;
double area () {
return getWidth() * getHeight() / 2 ;
void showStyle() {
System. out . println( " Triangle is"+ style);
class Shapes3 {
public static void main(String args[)) {
Triangle t1 new Tri angle( " filled", 4.0, 4 . 0) ;
Triangle t2 = new Triangle( " outlined", 8 . 0, 12.0);
System. out .println("Info for tl: " );
t1 . showStyle ();
t1 . showDim () ;
System. out.println( "Area is " + tl . area()) ;
System. out . println() ;
System.out . println( " Info for t2: " );
t2 . showStyle() ;
t2 . showDim( );
System. out.println( "Area is " + t2.area()) ;
El constructor de Triangle inicializa los miembros de TwoDClass que hereda junto
a su propio campo style.
Cuando tanto la superclase como la subclase definen constructores, el proceso es ms
complicado ya que es necesario ejecutar los dos constructores. En este caso debe usar otra
palabra clave de Java, super, que tiene dos formatos. El primero invoca un constructor
de superclase. El segundo se usa para acceder a un miembro de la superclase oculto por
un miembro de una subclase. Veamos el primer caso.
Utilizar super para invocar constructores
de superclase
Una subclase puede invocar un constructor definido por su superclase por medio de_
siguiente formato de super:
super(lista-parmetros);
Java 7 fjiji
lista-parmetros especifica los parmetros que necesita el constructor de la
superclase. super () siempre debe ser la primera instruccin ejecutada dentro de un
constructor de subclase. Para ver el uso de super (),la siguiente versin de TwoDShape
define un constructor que inicializa width y height.
I Aadir constructores a TwoDShape.
class TwoDShape {
private double width;
private double height;
11 Constructor con parmetros .
TwoDShape(double w, double h) {
width = w;
height = h;
11 Mtodos de acceso para widtj y height .
double getWidth() { return width; }
double getHeight() { return height; }
void setWidth(double w) { widtj = w; }
void setHeight(double h) { height = h;
void showDim() {
System. out.println( " Width and height are " + width + " and" + height);
11 Una subclase de TwoDShape para tringulos .
class Triangle extends TwoDShape (
private String style;
Triangle(String s, double w, double h)
super(w, h); //invocar constructor de la superclase
style = s ;
double a rea () {
return getWidth() * getHeight() I 2;
void showStyle() {
System.out . println("Triangle is " + style);
class Shapes4 {
public static void main(String args( ] ) {
Triangle tl new Triangle( " filled", 4.0, 4.0);
Triangl e t2 = new Triangle( "outlined", 8 . 0, 12 . 0);
System. out.println("Info for tl : ");
tl . showStyle();
tl.showDim();
System.out.println("Area is"+ tl.ar ea());
7. Herencia
System. out.pr intln() ;
System. out.println( "Info for t2: " );
t2.showStyle() ;
t2 . showDim( ) ;
System. out . println( " Area is " + t2.area()) ;
Triang le () invoca supe r () con los parmetros w y h. Esto provoca la invoca-
cin del constructor TwoDShape () , que inicializa wi dth y h e i g ht con estos valores.
Tr iangle ya no los inicializa. Slo tiene que inicializar su valor exclusivo: styl e . De
este modo TwoDShape puede crear su subobjeto como desee. Es ms, TwoDSh ape puede
aadir funcionalidad y las subclases existentes no lo sabrn, lo que impide que se altere
el cdigo existente.
super () puede invocar cualquier constructor definido por la superclase. El cons-
tructor ejecutado ser el que coincida con los argumentos. Por ejemplo, las siguientes
versiones ampliadas de TwoDShape y Tri a ngle incluyen constructores predetermi-
nados y constructores que aceptan un argumento:
11 Aadir ms constructores a TwoDShape .
class TwoDShape {
private double width;
private double height ;
11 Un constructor predeterminado .
TwoDShape() {
width = height = O. O;
11 Constructor con parmetros.
TwoDShape(double w, double h) {
width = w;
height = h;
11 Crear objeto con la misma anchura y altura .
TwoDShape (double x) {
width = height = x;
11 Mtodos de acceso para width y height .
double getWidth() { return width; }
double getHeight() { return height; }
void setWidth(double w) { width = w; }
void setHeight(double h) { height = h;
void showDim() {
System. out . println( " Width and height are " + width + "and" + height);
11 Una subclase de TwoDShape para tringulos.
class Triangle extends TwoDShape {
private String style;
// Un constructor predeterminado.
Triangle ()
super();
style = " none";
11 Constructor
Triangle(String s, double w, double h)
super(w, h); //invocar constructor de la superclase
style = s ;
// Un constructor con un argumento .
Triangle(double x)
super(x); //invocar constructor de la superclase
style = "filled";
double area ()
return getWidth() * getHeight() / 2;
void showStyle()
System. out .println( "Triangle is"+ style);
class ShapesS {
public static void main(String args[])
Triangle tl new Triangle();
Triangle t2
Triangle t3
tl = t2 ;
new Triangle( " outlined", 8.0, 12 . 0) ;
new Triangle(4.0);
System. out . println("Info for tl : " );
tl.showStyle();
tl.showDim();
System.out.println( "Area is " + tl.area( ));
System. out.println();
System.out . println( " Info for t2 : " );
t2 . showStyle ();
t2 . showDim ();
System. out . println( "Area is " + t 2 . area()) ;
System. out . println() ;
System. out.println( " Info for t3: ");
t3 . showStyle();
t3.showDim();
Java 7 f..19-jl
f.JOfj 7. Herencia
System. out . pri ntln ("Ar ea is " + t3 . area( ));
System. out. println ();
Esta versin genera el resultado mostrado en la figura 7.2.
Figura 7.2. Ejecucin de TwoDShape con ms constructores.
Repasemos los conceptos bsicos de super ().Cuando una subclase invoca super (),
invoca el constructor de su superclase inmediata. Por tanto, super () siempre hace refe-
rencia a la superclase inmediatamente por encima de la clase invocadora, incluso en una
jerarqua multinivel. Adems, super ( ) siempre debe ser la primera instruccin ejecu-
tada dentro del constructor de una subclase.
Utilizar super para acceder a miembros
de una superclase
Existe una segunda forma de super que acta como this, pero que siempre hace
referencia a la superclase de la subclase en la que se usa. Su formato general es el
siguiente:
super . miembro
miembro puede ser un mtodo o una variable de instancia. Esta forma de super
suele aplicarse cuando los nombres de miembros de una subclase ocultan miembros con
el mismo nombre en la superclase. Veamos una sencilla jerarqua de clases:
11 Usar super para evitar nombres ocultos .
class A {
int i ;
11 Crear una subclase ampliando la clase A.
class B extends A
int i ; // esta i oculta la i de A
B (int a , int b) {
super.i = a ; // i en A
i = b ; // i en B
void show() {
System.out . println( " i in superclass : " + super.i) ;
System.out . println( " i in subclass: " + i);
class UseSuper {
public static void main(String args[]) {
B subOb = new B(l , 2) ;
subOb.show() ;
Este programa muestra el siguiente resultado:
i in superclass: 1
i in subclass : 2
Java 7 f;Jeji
Aunque la variable de instancia i de B oculta i en A, super permite el acceso a la i
definida en la superclase. super tambin se puede usar para invocar mtodos ocultos
por una subclase.
Ejercicio 7 .1. Ampliar la clase Vehicle
Para ilustrar el poder de la herencia, ampliaremos la clase Vehicle del captulo
4. Como recordar, Vehicle contiene informacin sobre vehculos, como el nmero
de pasajeros que admiten, la capacidad de su depsito y el consumo de combustible.
Podemos usar la clase Vehicle como punto de partida de otras clases ms especiali-
zadas. Por ejemplo, un tipo de vehculo es una furgoneta. Un atributo importante de
una furgoneta es su capacidad de carga. Por tanto, para crear una clase Truck, puede
ampliar Vehicle y aadir una variable de instancia que almacene su capacidad de
carga, como sucede en la siguiente versin de Vehicle. En el proceso, las variables de
instancia de Vehicle se convierten en private y se definen mtodos de acceso para
recuperar y establecer sus valores.
l. Cree el archivo TruckDemo . java y copie la ltima implementacin de Vehicle
del captulo 4.
2. Cree la clase Truck como se indica a continuacin:
//Ampliar Vehicle para crear una especializacin Truck .
class Truck extends Vehicle {
private int cargocap; // capacidad de carga en libras
f,19ji 7. Herencia
11 Un constructor de Truck .
Truck(int p, int f , int m, int c) {
/* Inicializar miembros de Vehicle con
el constructor de Vehicle. */
super(p, f , m);
cargocap = c ;
11 Mtodos de acceso para cargocap .
int getCargo() { return cargocap;
void putCargo (int c) { cargocap = c;
Truck se hereda de Vehicle y aade cargocap, getCargo () y pu tCargo () .
Por tanto, Truck incluye todos los atributos generales de vehculos definidos por
Vehicle. Slo tiene que aadir los elementos que sean exclusivos de su propia
clase.
3. Convierta en pri vate las variables de instancia de Vehicle:
private int passengers; // nmero de pasajeros
private int fuelcap; // capacidad de combustible en galones
private int mpg; // consumo de combustible en millas por galn
4. Veamos el programa completo que ilustra la clase Truck:
// Ejercicio 7 . 1
11
// Crear una subclase de Vehicle para camionetas.
class Vehicle {
private int passengers ; // nmero de pasajeros
private int fuelcap; // capacidad de combustible en galones
private int mpg; // consumo de combustible en millas por galn
// Constructor de Vehicle .
Vehicle(int p , int f , int m)
passengers = p ;
fuelcap = f ;
mpg = m;
// Devolver la autonoma .
int range () {
return mpg * fuelcap;
// Calcular combustible necesario para una determinada distancia .
double fuelneeded(int miles ) {
return (double) miles / mpg;
//Mtodos de acceso para variables de instancia .
int getPassengers() { return passengers; }
void setPassengers(int p) ( passengers = p; )
int getFuelcap() { return fuelcap; }
void setFuelcap(int f) { fuelcap = f; }
int getMpg () { return mpg; }
void setMpg(int m) { mpg = m; }
// Ampliar Vehicle para crear una especializacin Truck .
class Truck extends Vehicle {
private int cargocap; // capacidad de carga en libras
// Un constructor de Truck .
Truck (int p, int f , int m, int c) {
/* Inicializar miembros de Vehicle con
el constructor de Vehicle. */
super (p, f , m);
cargocap = c ;
11 Mtodos de acceso para cargocap.
int getCargo() { return cargocap; }
void putCargo(int c ) { cargocap = c;
class TruckDemo
public static void main(String args[]) {
11 crear camionetas
Truck semi = new Truck(2 , 200 , 7 , 44000);
Truck pickup= new Truck(3 , 28, 15, 2000 );
double gallons;
int dist = 252;
gallons = semi.fuelneeded(dist);
Java 7 f .tf $j
System. out.println( " Semi can carry " + semi.getCargo() + " pounds .'' );
System.out.println( '' To go " + dist + " miles semi needs " + gallons + "
gallons of fuel . \n" );
gallons = pickup.fuelneeded(dist);
System.out . println( " Pickup can carry " + pickup.getCargo() +
" pounds. " ) ;
System.out . println( " To go " + dist + " miles pickup needs '' + gallons +
"gallons of fuel. ");
5. Este programa genera el resultado mostrado en la figura 7.3.
6. Se pueden derivar otros tipos de clases de Vehicle. Por ejemplo, la siguiente
estructura crea una clase que almacena la altura de un vehiculo:
f j*j 7. Herencia
11 Crear una clase de vehculo
class OffRoad extends Vehicle {
prvat e int groundClearance; // altura en pulgadas
11 .. .
El aspecto que recordar es que tras crear una superclase que define los aspectos
generales de un objeto, se puede heredar de la misma para crear clases especiali-
zadas. Cada subclase aade sus propios atributos exclusivos. Es la esencia de la
herencia.
Figura 7.3. Ejemplo de subclase.
Crear una jerarqua multinivel
Hasta el momento, hemos usado sencillas jerarquas de clases formadas por una
superclase y una subclase. Sin embargo, puede disear jerarquas que contengan todos
los niveles de herencia que desee. Corno hemos mencionado, se puede usar una subclase
corno superclase de otra. Por ejemplo, dadas las clases A, B y e, e puede ser una subclase
de B, que es una subclase de A. En estos casos, cada subclase hereda todos los rasgos de
todas sus superclases. En el ejemplo, e hereda todos los aspectos de B y A.
Para comprobar la utilidad de una jerarqua multinivel, fjese en el siguiente programa
La subclase Triangle se usa corno superclase para crear la subclase ColorTriangle,
que hereda todos los rasgos de Triangle y TwoDShape, y aade el campo color, que
contiene el color del tringulo.
11 Una jerarqua multinivel.
class TwoDShape {
prvate double width;
prvate double height;
11 Constructor predeterminado.
TwoDShape() {
width = height = O.O;
11 Constructor con parmetros .
TwoDShape(double w, double h) {
width = w;
height h ;
// Crear objeto con la misma altura y anchura .
TwoDShape(double x)
width = height = x;
//Mtodos de acceso para width y height.
double getWidth() { return width; }
double getHeight() { return height; }
void setWidth(double w) { width = w; }
void setHeight(double h) { height = h;
void showDim() {
Java 7 tifl
System.out.println( " Width and height are " + width + "and" + height);
/ Ampliar TwoDShape .
class Triangle extends TwoDShape
private String style;
//Un constructor predeterminado.
Triangle() {
super() ;
style = " none ";
Triangle(String s , double w, double h)
super(w, h); // invocar constructor de la superclase
style = s;
11 Constructor con un argumento.
Triangle(double x) {
super(x); //invocar constructor de la superclase
style = "fi lled";
double area() {
return getWidth() * getHeight() / 2 ;
void showStyle( ) {
System.out . println( "Triangle is " + style);
11 Ampliar Triangle.
class ColorTriangle extends Triangle { // ColorTriangle hereda Triangle, que
// desciende de TwoDShape, por lo que ColorTriangle incluye todos
// los miembros de Triangle y TwoDShape
private String color ;
fjei:I 7. Herencia
ColorTriangle(String c , String s,
double w, double h) {
super (s , w, h) ;
color = c;
String getColor() { return color; }
void showColor() {
System. out . println( "Color is " + color);
class Shapes6 {
public static void main(Stri ng args[]) {
ColorTriangle tl =
new ColorTriangle( " Blue", " outlined", 8.0, 12.0);
ColorTriangle t2 =
new ColorTriangle( " Red" , "filled", 2.0, 2 . 0);
System.out . println( " Info for tl : " );
tl . showStyle();
tl . showDim() ;
tl . showColor();
11 un objeto ColorTriangle puede invocar los mtodos que
11 defina y los de su superclase
System.out.println( " Area is " + tl.area()) ;
System. out.pri ntln() ;
System.out.println( " Info for t2 : " );
t2.showStyle();
t2 . showDim();
t2.showColor();
System. out . println( " Area is"+ t2 . area());
En la figura 7.4 puede ver el resultado creado por este programa.
iii Smbolo (fel sistema
Figura 7.4. Ejemplo de jerarqua multinivel.
Java 7 f..19fl
Gracias a la herencia, ColorTriangle puede usar las clases Triangl e y TwoDShape
previamente definidas y aadir slo la informacin que necesite para su aplicacin
concreta. Es parte del valor de la herencia: permitir la reutilizacin del cdigo.
Este ejemplo ilustra otro aspecto importante: super () siempre hace referencia al
constructor de la superclase ms prxima. En Co l o rTriangle, super () invoca el cons-
tructor de Triangle. En Triangle, super () invoca el constructor de TwoDShape. En
una jerarqua de clases, si el constructor de una superclase requiere parmetros, todas
las subclases deben pasar dichos parmetros en el camino, independientemente de que
la subclase necesite sus propios parmetros.
Cundo invocar constructores
En el anlisis de la herencia y las jerarquas de clases, seguramente se haya pregun-
tado qu sucede al crear un objeto de superclase, si se invoca primero su constructor o el
de la superclase. Por ejemplo, en una subclase By una superclase A, se invoca el cons-
tructor de A antes que el de B o al contrario? En una jerarqua de clases, los constructores
se invocan en orden de derivacin, de superclase a subclase. Es ms, como s up er ( )
debe ser la primera instruccin ejecutada en el constructor de una subclase, el orden es
el mismo se use o no super ().Si no se usa super (),se ejecuta el constructor prede-
terminado (sin parmetros) de cada superclase. El siguiente programa ilustra cundo
se ejecutan los constructores:
11 Ejemplo de invocacin de constructores .
11 Crear una superclase .
class A {
A() {
System. out.println( " Constructing A. " ) ;
11 Crear una subclase ampl iando l a clase A.
class B extends A {
B () {
System. out . println( " Constructing B." ) ;
11 Crear otra subclase ampliando B.
class C extends B {
c () 1
System. out . println( " Constructing C. " ) ;
class OrderOfConstruction {
public static void main(String args[]) {
C c = new C( );
}
f '1ij1i 7. Herencia
Este programa genera el siguiente resultado:
Constructing A.
Constructing B.
Constructing C.
Como puede apreciar, los constructores se invocan en orden de derivacin.
Si lo piensa, tiene sentido que los constructores se ejecuten en orden de derivacin.
Como una superclase no conoce las subclases, la inicializacin que tenga que realizar
es independiente de cualquier requisito previo de cualquier inicializacin que realice la
subclase. Por tanto, debe ejecutarse antes.
Referencias de superclase y objetos de subclase
Como ya sabe, Java es un lenguaje de tipos fuertes. Adems de las conversiones
estndar y las promociones automticas que aplica a sus tipos primitivos, la compati-
bilidad de tipos se aplica de forma estricta. Por tanto, una variable de referencia de un
tipo de clase no puede hacer referencia a un objeto de otro tipo de clase. Fjese en este
programa:
11 No es compilar .
class X {
int a;
X(int i) {a i ;
class Y {
int a;
Y (int i) { a i; }
class IncompatibleRef {
public static void main(String args[]) {
X x = new X( l) ;
X x2;
Y y= new Y(5);
x2 x; // Correcto, son del mismo tipo
x2 y; // Error, no son del mismo tipo
Aunque las clases X e Y sean fsicamente iguales, no se puede asignar una referencia
a X a un objeto Y ya que tienen tipos diferentes. Por lo general, una variable de refe-
rencia de objeto slo puede hacer referencia a objetos de su tipo. Sin embargo, existe
una excepcin. Se puede asignar a una variable de referencia de una superclase una
Java 7 f.J$1
referencia a un objeto de cualquier subclase derivada de dicha superclase. Es decir, una
referencia de superclase puede hacer referencia a un objeto de subclase. Veamos un
ejemplo:
Una referencia de superclase puede hacer referencia a un objeto de subclase.
:::!.ass X {
int a;
X(int i) { a i ;
class Y extends X {
int b;
Y(int i , int j) {
super (j);
b = i ;
class SupSubRef {
public static void main(String args[]) {
X x = new X(l0);
X x2 ;
Y y= new Y(S , 6);
x2 = x ; // Correcto, son del mismo tipo
System. out.println( " x2 . a : " + x2.a);
x2 =y; // tambin correcto porque Y se deriva de X
System. out . println( " x2.a: "+ x2.a);
// X ahora slo hace referencia a miembros de X
x2 . a = 19; // OK
// x2.b = 27 ; // Error , X no tiene un miembro b
En este caso, Y se deriva de X; por tanto, se puede asignar a x2 una referencia a un
objeto Y. Es importante comprender que es el tipo de la variable de referencia, no el del
objeto al que hace referencia, el que determina a qu miembros se puede acceder. Es
decir, cuando se asigna una referencia a un objeto de subclase a una variable de referencia
de superclase, slo tiene acceso a las partes del objeto que defina la superclase. Por este
motivo x2 no puede acceder a b incluso al hacer referencia a un objeto Y. Si lo piensa,
tiene sentido, ya que la superclase no sabe a qu subclase se aade. Por este motivo, la
ltima lnea de cdigo se comenta.
Aunque la explicacin anterior puede resultar extraa, tiene importantes aplica-
ciones prcticas. Veremos una de ellas y la otra al final del captulo, cuando abordemos
el reemplazo de mtodos.
Un punto importante en el que las referencias de subclase se asignan a variables de
superclase es al invocar constructores en una jerarqua de clases. Como sabe, es habitual
que una clase defina un constructor que acepte como parmetro un objeto de la clase. De
f.tfj
7. Herencia
este modo la clase puede crear una copia del objeto. Las subclases de este tipo de clases
pueden aprovechar este hecho. Por ejemplo, las siguientes versiones de TwoDShape y
Tr iangle aaden constructores que aceptan un objeto como parmetro.
class TwoDShape {
prvate double width;
prvate double height;
11 Constructor predeterminado .
TwoDShape() {
width = height = O. O;
11 Constructor con parmetros .
TwoDShape(double w, double h) {
width = w;
height = h;
// Crear un objeto con la misma altura y anchura .
TwoDShape(double x)
width = height = x ;
11 Crear un objeto a partir de otro.
TwoDShape(TwoDShape ob)
width = ob. width;
height = ob.height;
11 Mtodos de acceso para width y height.
double getWidth() { return width; }
double getHeight() { return height; J
void setWidth(double w) { width = w; }
void setHeight(double h) { height = h;
void showDim() {
System.out . println("Width and height are " + width + " and " + height);
11 Un subclase de TwoDShape para triangles .
class Triangle extends TwoDShape {
prvate String style;
11 Constructor predeterminado.
Triangle () {
super();
style = "none " ;
}
// Constructor de Triangl e.
Triangle(String s , double w, double h) {
super(w, h); // invocar el constructor de la superclase
style s;
11 Constructor con un argumento.
Triangle(double x) {
super(x); // invocar el constructor de la superclase
style = "filled";
// Crear un objeto a partir de otro.
Triangle(Triangle ob) {
super(ob) ; //pasar objeto al constructor de TwoDShape
style = ob.style ;
double area( ) {
return getWidth() * getHeight() / 2;
void showStyle() {
System. out.println (" Triangle is " + style);
class Shapes7 {
public static void main(String args [] ) {
Triangle tl =
new Triangle("outlined", B.O, 12.0);
11 crear una copia de tl
Triangle t2 = new Triangle(tl);
System. out . println( " Info for tl: ") ;
tl.showStyle ();
tl . showDim( );
System. out.println( " Area is "+ tl . area());
System. out .println();
System. out . println( " Info for t2 : ");
t2 . showStyle() ;
t2.showDim() ;
System. out . println( " Area is " + t2.area());
Java 7 f.Z$1
En este programa, se crea t 2 a partir de tl, por lo que son idnticos (vase la figura
7.5). Fjese especialmente en este constructor Triangle:
// Crear un objeto a partir de otro .
Triangle(Triangle ob) {
super (ob); //pasar objeto al constructor de TwoDShape
style = ob.style ;
f.1$1 7. Herencia
Figura 7.5. Resultado generado por el programa.
Recibe un objeto de tipo Triangle y los pasa, a travs de super, a este constructor
Two DShape:
11 Crear un objeto a partir de otro .
TwoDShape(TwoDShape ob)
width = ob . width;
height = ob . height;
Two DShape () espera un objeto TwoDShape. Sin embargo, Triangle () pasa un
objeto Triangle. Funciona porque, como hemos dicho, una referencia de superclase
puede hacer referencia a un objeto de subclase. Por tanto, se puede pasar a TwoDSha pe ()
una referencia a un objeto de la clase derivada de TwoDSha pe. Como el constructor
TwoDShape () slo inicializa las partes del objeto de subclase que son miembros de
TwoDShape, no importa que el objeto tambin contenga otros miembros aadidos por
clases derivadas.
Reemplazar mtodos
En una jerarqua de clases, cuando un mtodo de una subclase tiene el mismo tipo de
devolucin y firma que un mtodo de su superclase, el mtodo de la subclase reemplaza
al de la superclase. Al invocar un mtodo reemplazado desde una subclase, siempre
hace referencia a la versin del mtodo definida por la subclase. La versin del mtodo
definida por la superclase se oculta. Veamos un ejemplo:
11 Reemplazar mtodos.
class A {
int i , j ;
A (int a , int b) {
i a;
j = b ;
11 mostrar i y j
void show() {
System. out . println( " i and j : " + i + " " + j) ;
=:ass B extends A {
int k ;
B (int a , int b , int c) {
super (a , b) ;
k = c ;
// mostrar k ; r eemplaza show() en A
void show() {
System.out . println (" k : " + k) ;
c lass Override {
public static void main(String args[]) {
B subb = new B(l , 2 , 3);
subb . show() ; //invoca show() en B
Este programa genera el siguiente resultado:
k: 3
Java 7 f .Jdlj
Al invocar show () en un objeto de tipo B, se usa la versin de show () definida en
B. Es decir, la versin de show () en B reemplaza la versin declarada en A.
Si desea acceder a la versin de superclase de un mtodo reemplazado, puede usar
super. Por ejemplo, en esta versin de B, se invoca la versin de superclase de show ()
desde la versin de la subclase. Esto permite que se muestren todas las variables de
instancia.
class B extends A {
int k;
B (int a , int b , int c) {
super(a, b) ;
k = c ;
void show() {
super . show() ; // invoca sho w() de A
System. out.pri ntln( " k : " + k ) ;
Si sustituye esta versin de show () en el programa anterior, ver el siguiente resul-
tado:
i and j : l 2
k : 3
Aqu, super . s h ow ( ) invoca la versin de superclase de show ( ) .
f 'ldl 7. Herencia
El reemplazo de mtodos slo se produce cuando las firmas de los dos mtodos son
idnticas. Si no lo son, los dos mtodos se sobrecargan. Fjese en la versin modificada
del ejemplo anterior:
/* Los mtodos con firmas diferentes se
sobrecargan, no se reemplazan . */
class A {
int i , j ;
A(int a , int b) {
i = a;
j = b;
11 mostrar i y j
void show ( ) {
System. out . println (" i and j : " + i + " " + j);
11 Crear una subclase ampliando la clase A.
class B extends A {
int k ;
B (int a, int b , int c) {
super(a, b) ;
k = c;
}
11 sobrecargar show()
void show (String msg)
System. out . println(msg + k);
class Overload {
public static void main(String args[]) {
B subOb = new B(l , 2 , 3);
subOb . show( " This is k : " ); 11 invoca show() en B
subOb . show(); // invoca show() en A
Este programa genera el siguiente resultado:
This is k : 3
i and j : 1 2
La versin de s h ow () en B acepta un parmetro de cadena, lo que distingue esta
firma de la de A, que no acepta parmetros. Por tanto, no se produce reemplazo.
Los mtodos reemplazados admiten polimorfismo
Aunque los ejemplos del apartado anterior ilustran los mecanismos del reemplazo de
mtodos, no muestran toda su potencia. En realidad, si slo fuera una simple conven-
cin de espacio de nombres, sera una curiosidad pero sin valor real. Pero no es el caso.
Java 7 f.Zfl
El reemplazo de mtodos es la base de uno de los conceptos ms potentes de Java: la
entrega dinmica de mtodos. Es el mecanismo mediante el cual una invocacin a un
mtodo reemplazado se resuelve en tiempo de ejecucin y no durante la compilacin.
La entrega dinmica de mtodos es importante por ser la forma de implementar el poli-
morfismo en Java.
Comenzaremos recordando un importante principio: una variable de referencia de
superclase puede hacer referencia a un objeto de subclase. Java usa este hecho para
resolver invocaciones de mtodos reemplazados en tiempo de ejecucin. Veamos cmo. Al
invocar un mtodo reemplazado a travs de una referencia de superclase, Java determina
qu versin del mtodo ejecutar en funcin del tipo de objeto al que se haga referencia al
producirse la invocacin. Por tanto, esta determinacin se realiza en tiempo de ejecucin.
Cuando se hace referencia a distintos tipos de objetos, se invocan distintas versiones de
un mtodo reemplazado. Es decir, el tipo del objeto al que se hace referencia determina
la versin del mtodo reemplazado que ejecutar, no el tipo de la variable de referencia.
Por tanto, si una superclase contiene un mtodo reemplazado por una subclase, al hacer
referencia a distintos tipos de objetos a travs de una variable de referencia de superclase,
se ejecutan distintas versiones del mtodo.
Veamos un ejemplo que ilustra la entrega dinmica de mtodos:
11 Entrega dinmica de mtodos .
class Sup {
void who() {
System.out . println( " who() in Sup" );
class Subl extends Sup {
void who() {
System. out . println ( " who () in Subl " ) ;
class Sub2 extends Sup {
void who() {
System. out. println ( " who () in Sub2" ) ;
class DynDispDemo {
public static void main(String args[]) {
Sup superOb new Sup();
Subl subObl new Subl();
Sub2 sub0b2 new Sub2();
Sup supRef;
supRef = superOb;
supRef.who(); //en cada caso, la version de who() que invocar se determina
11 en tiempo de ejecucin por el tipo de objeto al que se haga referencia
supRef = subObl;
f 1d:I 7. Herencia
supRef . who() ;
supRef = sub0b2 ;
supRef . who() ;
Este programa genera el siguiente resultado:
who() in Sup
who() in Subl
who () in Sub2
Este programa crea la superclase Sup y dos subclases de la misma, Subl y Sub2. Sup
declara el mtodo who () y las subclases lo reemplazan. Dentro de main (),se declaran
objetos de tipos Sup, Subl y Sub2. Adems, se declara una referencia de tipo Sup,
supRef. Tras ello, el programa asigna una referencia a cada tipo de objeto a supRef y
la usa para invocar who () . Como muestra el resultado, la versin ejecutada de who ()
se determina por el tipo de objeto al que se hace referencia durante la invocacin, no por
el tipo de clase de supRef.
Preguntas al experto
P: Los mtodos reemplazados de Java se parecen a las funciones virtuales de C++.
Existe alguna semejanza?
R: S, los lectores familiarizados con C++ reconocern que los mtodos reemplazados
de Java tienen un funcionamiento similar a las funciones virtuales de C++.
Por qu reemplazar mtodos
Como mencionamos antes, los mtodos reemplazados permiten a Java admitir el
polimorfismo de tiempo de ejecucin. El polimorfismo es esencial para la programacin
orientada a objetos ya que permite que una clase general pueda especificar mtodos
que sean comunes a todas sus derivadas, al tiempo que las subclases puedan definir la
implementacin especfica de algunos o todos estos mtodos. Los mtodos reemplazados
son otra forma de implementar el aspecto de una interfaz, mltiples mtodos del poli-
morfismo en Java. Parte de la clave en aplicar correctamente el polimorfismo consiste en
comprender que las superclases y subclases forman una jerarqua que pasa de menor a
mayor especializacin. Si se usa correctamente, la superclase ofrece todos los elementos
que una subclase puede usar directamente. Tambin define los mtodos que la clase
derivada debe implementar por su cuenta. De este modo la subclase puede definir sus
propios mtodos pero formar una interfaz coherente. Por tanto, al combinar la herencia
con los mtodos reemplazados, una superclase puede definir la forma general de los
mtodos que se usarn en todas sus subclases.
Java 7 f.1001
Aplicar reemplazo de mtodos a TwoDShape
Para comprender la potencia del reemplazo de mtodos, lo aplicaremos a la clase
TwoDShape. En los ejemplos anteriores, cada clase derivada de TwoDShape defina
un mtodo a rea (),lo que sugiere que resultara ms adecuado que a rea () formara
parte de la clase TwoDShape y que cada clase lo reemplazara y determinara el clculo
del rea para el tipo de forma que encapsule. El siguiente programa se encarga de ello.
Tambin se aade un campo de nombre a TwoDShape, lo que facilita crear programas
de ejemplo.
11 Usar entrega dinmica de mtodos .
class TwoDShape
private double width;
private double height ;
private String name;
11 Constructor predeterminado.
TwoDShape () {
width = height = O. O;
name = "none ";
11 Constructor con parmetros .
TwoDShape(double w, double h, String n) {
width = w;
height = h ;
name = n;
11 Crear objeto con la misma altura y anchura .
TwoDShape(double x, String n) {
width = height = x;
name = n;
11 Crear un objeto a partir de otro.
TwoDShape(TwoDShape ob) {
width = ob.width;
height = ob.height;
name = ob . name ;
11 Mtodos de acceso para width y height .
double getWidth() { return width; }
double getHeight () { return height; }
void setWidth(double w) { width = w; }
void setHeight(double h) { height = h;
String getName() { return name; }
void showDim() {
System.out . println( " Width and height are " + width + " and " + height);
,,,,,
7. Herencia
double area ( ) \ 11 el mtodo area() definido por TwoDShape
System. out . println ( "area () must be overridden") ;
return O. O;
11 Una subclase de TwoDShape para tringulos.
cla ss Triangle extends TwoDShape {
private String style;
// Constructor predeterminado.
Triangle ()
super();
style = " none ";
// Constructor para Triangle .
Triangle (String s , double w, double h)
super (w, h , " triangle" ) ;
style = s ;
11 Constructor de un argumento .
Triangle(double x) {
super (x , " triangle" ); //invocar el constructor de la superclase
style = " filled";
// Crear un objeto a partir de otro .
Triangle(Triangle ob) {
super(ob); //pasar objeto al constructor je TwoDShape
style = ob.style;
11 Reemplazar area() para Triangle .
double area() {
return getWidth() * getHeight() / 2;
void showStyle()
System. out . println( " Triangle is " + style);
// Una subclase de TwoDShape para rectngulos .
class Rectangle extends TwoDShape
// Constructor predeterminado.
Rectangle ( )
super();
11 Constructor de Rectangle.
Rectangle(double w, double h) {
super(w, h, '' rectangle" ) ; //invocar el constructor de la superclase
11 Crear un cuadrado.
Rectangle(double x) {
super(x, " rectangle'' ); //invocar el constructor de la superclase
11 Crear un objeto a partir de otro.
Rectangle(Rectangle ob) {
super (ob) ; //pasar objeto al constructor de TwoDShape
boolean isSquare () {
if(getWi dth() == getHeight()) return true;
return false ;
11 Reemplazar area() para Rectangle .
double area () {
return getWidth() * getHeight( ) ;
class DynShapes {
public static void main (String args[]) {
TwoDShape shapes[] = new TwoDShape[S];
shapes[O] new Triangle( " outlined" , 8 . O,
shapes[l] new Rectangle(lO) ;
shapes[2] new Rectangle(lO, 4) ;
shapes[3] new Triangle(7 . 0) ;
12. o) ;
shapes[4] new TwoDShape(lO, 20, " generic" ) ;
for(int i=O; i < shapes . length; i++) {
Java 7 fJjl
System. out.println( " object is " + shapes[i] . getName());
System.out.println( "Area is " + shapes[i] .area()); //en cada forma se
11 invoca la versin adecuada de area()
System. out.println();
Este programa genera el resultado mostrado en la figura 7.6.
Analicemos el programa. Por un lado, como hemos indicado, a rea () forma parte de
la clase TwoDShap e yse reemplaza en Triangle y Rectangle. Dentro de TwoDShape,
are a () recibe una implementacin de marcador de posicin que simplemente informa
al usuario de que este mtodo debe ser reemplazado por una subclase. Cada reemplazo
de area () proporciona una implementacin adecuada para el tipo de objeto encapsu-
lado por la subclase. Por tanto, si implementara una elipse, a rea () tendra que calcular
el rea de una elipse.
En el programa, en rnain (), shapes se declara como matriz de objetos TwoDShape.
Sin embargo, se asignan referencias Triangl e, Rectangle y TwoDShape a los elementos
de esta matriz. Es correcto ya que una referencia de superclase puede hacer referencia a
f if I 7. Herencia
un objeto de subclase. Tras ello, el programa itera por la matriz y muestra informacin
sobre cada objeto. Aunque sea sencillo, el programa ilustra la potencia de la herencia y el
reemplazo de mtodos. El tipo de objeto al que hace referencia la variable de referencia
de superclase se determina en tiempo de ejecucin. Si un objeto se deriva de TwoDShape,
se puede obtener su rea mediante la invocacin de a rea ().La interfaz de esta opera-
cin es la misma independientemente del tipo de forma usado.
Figura 7.6. Ejemplo de entrega dinmica de mtodos.
Utilizar clases abstractas
En ocasiones necesitar crear una superclase que solamente defina una forma gene-
ralizada que compartir en todas sus subclases, para que cada una complete los detalles.
Dicha clase determina la naturaleza de los mtodos que la subclase debe implementar
pero, por s misma, no ofrece una implementacin de uno o varios de dichos mtodos.
Esta situacin puede darse cuando una superclase no pueda crear una implementacin
con sentido de un mtodo. Es lo que sucede con la versin de TwoDShape del ejemplo
anterior. La definicin de a rea () es simplemente un marcador de posicin. No calcula
ni muestra el rea de ningn tipo de objeto.
Como veremos al crear bibliotecas de clases, un mtodo puede carecer de una defi-
nicin descriptiva en el contexto de su superclase. Puede solucionarlo de dos formas_
Por un lado, como vimos en el ejemplo anterior, puede hacer que muestre un mensaje
de advertencia. Aunque es un enfoque til en determinados casos, como en
no suele ser apropiado. Puede tener mtodos que la subclase deba reemplazar para que
tenga sentido. Fjese en la clase Tria ngl e . Es incompleta si area () no se define. En
este caso, necesita una forma de garantizar que una subclase reemplaza los mtodos
necesarios. La solucin de Java a este problema son los mtodos abstractos.
Un mtodo abstracto se crea especificando el modificador de tipo a b s tract. Carece
de cuerpo y, por tanto, la superclase no lo implementa. Por ello, debe reemplazarlo una
subclase, no puede usar la versin definida en la superclase. Para declarar un mtodo
abstracto, use este formato:
abstract tipo nombre (lista-parmetros ) ;
Java 7 fJ#i
Como puede apreciar, no hay cuerpo del mtodo. El modificador abstract slo
se puede usar en mtodos normales, no se puede aplicar a mtodos estticos ni a cons-
tructores.
Una clase que contenga uno o varios mtodos abstractos tambin debe declararse
como abstracta aadiendo el modificador abstract por delante de su declaracin class.
Por ello, al intentar crear un objeto de una clase abstracta por medio de new se genera
un error de tiempo de compilacin.
Cuando una subclase hereda una clase abstracta, debe implementar todos los mtodos
abstractos de la superclase. En caso contrario, la subclase tambin debe especificarse
como abstract. Por tanto, el atributo abstract se hereda hasta que se logre una
implementacin completa.
Por medio de una clase abstracta puede mejorar la clase TwoDShape. Como no existe
concepto de rea para una figura indefinida de dos dimensiones, la siguiente versin del
programa anterior declara a rea () como abstract dentro de TwoDShape y TwoDShape
como abstract. Evidentemente, significa que todas las clases derivadas de TwoDShape
deben reemplazar area ().
11 Crear una clase abstracta.
abstract class TwoDShape {
private double width;
private double height ;
private String name;
11 Const r uctor predeterminado .
TwoDShape() {
width = height = O.O;
name = "none";
11 Constructor con parmetros.
TwoDShape(double w, double h, String n) {
width = w;
hei ght = h;
name = n ;
11 Crear objeto de la misma altura y anchura .
TwoDShape(double x, String n) {
width = height = x;
name = n;
11 Crear un objeto a partir de otro .
TwoDShape(TwoDShape ob) {
width = ob.width ;
height = ob . hei ght ;
name = ob .name ;
11 Mtodos de acceso para width y height .
double getWidth() { return width; }
double getHeight() { return height ; }
ftt
7. Herencia
void setWidth(double w) { width = w; }
void setHeight(double h) { height = h;
String getName () { return name ; }
void showDim ()
System.out . println( " Width and height are"+ width + " and " + height) ;
//Ahora, area() es abstracto.
abstract double area();
11 Una subclase de TwoDShape para tringulos .
class Triangle extends TwoDShape {
private String style;
11 Constructor predeter minado .
Triangle ()
super();
style = " none";
11 Constructor para Triangle .
Triangle(String s , double w, double h)
super (w, h , " triangle" ) ;
style = s;
11 Constructor de un argumento .
Triangle(double x) {
super(x, " triangle" ); //invocar constructor de la superclase
style = " filled" ;
//Crear un objeto a partir de otro.
Triangle(Triangle ob)
super(ob); 11 pasar objeto al constructor de TwoDShape
style = ob.style ;
double a rea ()
return getWidth( ) * getHeight() / 2;
void showStyle()
System.out . println( " Triangle is " + style);
11 Una subclase de TwoDShape para rectngulos .
class Rectangle extends TwoDShape {
// Constructor predeterminado.
Rectangle ()
super() ;
// Constructor de Rectangle .
Rectangle(double w, double h)
super(w, h , " rectangle" ) ; //invocar constructor de la superclase
// Crear un cuadrado .
Rectangle (double x ) {
super(x, " rectangle" ) ; //invocar constructor de la superclase
// Crear un objeto a partir de otro .
Rectangle (Rectangle ob) {
super(ob); // pasar ob jeto al constructor de TwoDShape
boolean i sSquare() {
if(getWidth() == getHeight()) return true;
return false ;
double area( ) {
return getWidth() * getHeigh t() ;
class AbsShape {
public static void main (String args [ ] ) {
TwoDShape shapes[] = new TwoDShape[4] ;
shapes[O]
shapes[l)
shapes[2]
shapes[3]
new Triangle( " outlined", 8 . 0 , 12.0);
new Rectangle(lO) ;
new Rectangle(lO, 4) ;
new Triangle(7 . 0) ;
for(int i =O ; i < shapes . length ; i++) {
System.out.println( " object is " + shapes[i] . getName());
System. out . println( " Area is " + shapes[i) . area() );
System. out . println() ;
Java7 fitJ
Como ilustra el programa, todas las subclases de TwoDShape deben reemplazar
a rea () , Para comprobarlo, intente crear una subclase que no reemplace a rea ( ) . Recibir
un error de tiempo de compilacin. Evidentemente, se puede crear una referencia de
objeto de tipo TwoDShap e, lo que hace el programa. Sin embargo, no se pueden declarar
objetos de tipo TwoDShap e . Por ello, en main ( ) ,la matriz shapes se ha reducido a 4 y
ya no se crea un objeto TwoDShape. Un ltimo aspecto. TwoDShape sigue incluyendo los
mtodo showDim () y getName () , que abstract no modifica. Es totalmente vlida y,
fHI 7. Herencia
en realidad muy habitual, que una clase abstracta contenga mtodos concretos que una
subclase puede usar como tales. Slo los mtodos declarados como abstract deben
ser reemplazados en las subclases.
Utilizar final
A pesar de la potencia y la utilidad del reemplazo de mtodos y de la herencia, en
ocasiones tendr que evitarlos. Por ejemplo, puede tener una clase que encapsule el
control de un dispositivo de hardware. Esta clase puede permitir al usuario inicializar
el dispositivo y utilizar informacin privada y propietaria. En este caso, no conviene que
los usuarios de la clase puedan reemplazar el mtodo de inicializacin. Sea cual sea el
motivo, en Java puede impedir que un mtodo se reemplace o que una clase se herede
por medio de la palabra clave fin al.
Evitar reemplazos con final
Para evitar que un mtodo se reemplace, especifique fin al como modificador al
inicio de su declaracin. Los mtodos declarados como fin al no se pueden reemplazar.
El siguiente fragmento ilustra final:
class A {
final void meth() {
System.out.println( " This is a final method ." );
class B extends A {
void meth() { // ERROR! No se puede reemplazar .
System. out.println( " Illegal! " );
Como meth () se declara como final, no se puede reemplazar en B. Si intenta hacerlo,
se produce un error de compilacin.
Evitar la herencia con final
Puede evitar que una clase se herede si precede su declaracin con fin al. Al declarar
una clase como final, se declaran implcitamente todos sus mtodos como final. Como
habr imaginado, no se puede declarar una clase como abstract y final, ya que una
clase abstracta es incompleta por s misma y depende de sus subclases para proporcionar
la implementacin completa. Veamos un ejemplo de clase final :
final class A {
/ I .. .
Java 7 fiil
/ La siguie nte clase no es vlida.
class B extends A { // ERROR! No se puede crear subclase de A
11
Como indican los comentarios, B no puede heredar A ya que A se ha declarado como
final.
Utilizar final con miembros de datos
Adems de los usos anteriores de fina l , tambin se puede aplicar a variables
miembro para crear algo parecido a constantes con nombre. Si aade fi n a 1 por delante
del nombre de una variable de clase, su valor no se puede cambiar mientras dure el
programa. Evidentemente, puede asignar un valor inicial a esa variable. Por ejemplo,
en el captulo 6, vimos la clase Erro rMsg de administracin de errores. Dicha clase
asignaba una cadena legible a un cdigo de error. A continuacin, mejoramos la clase
original incluyendo constantes fina l que equivalen a los errores. Ahora, en lugar
de pasar a getErro rMsg () un nmero como 2, puede pasar la constante entera con
nombre DISKERR.
// Devolver un objeto String.
class ErrorMsg {
11 Cdigos de error .
fi nal int OUTERR = O;
final int INERR = l ; // declarar constantes f i nal
final int DISKERR = 2 ;
final int INDEXERR = 3 ;
String msgs[) = {
" Output Error",
" Input Error",
" Disk Full ",
" Index Out - Of- Bounds "
} ;
11 Devolver el mensaje de error.
String getErrorMsg(int i) {
if(i >=O & i < msgs . length)
return msgs[i);
el se
return " Invalid Error Code " ;
class FinalD {
public static void main(String args[))
ErrorMsg err = new ErrorMsg() ;
System.out . println(err.getErrorMsg(err.OUTERR)); // usar constantes final
System. out . println(err.getErrorMsg(err . DISKERR));
f H:I 7. Herencia
Fjese en el uso de las constantes final en main ().Como son miembros de la clase
ErrorMsg, se debe acceder a las mismas a travs de un objeto de esa clase. Evidentemente,
tambin se pueden heredar en subclases y usarlas para su acceso directo.
Como recurso estilstico, muchos programadores de Java usan identificadores en
maysculas para las constantes final, como en el ejemplo anterior, pero no es una
regla.
Preguntas al experto
P: Las variables miembro final se pueden convertir en static? se puede usar
final en parmetros de mtodos y variables locales?
R: La respuesta es s en ambos casos. Al convertir una variable miembro final en
static puede hacer referencia a la constante a travs de su nombre de clase en
lugar de un objeto. Por ejemplo, si las constantes de ErrorMsg se modificaran con
static, las instrucciones println () de main () tendran este aspecto:
System. out . println(err.getErrorMsg(ErrorMsg.OUTERR)) ;
System. out . println(err . getErrorMsg(ErrorMsg . DISKERR)) ;
Al declarar un parmetro como final se impide que se cambie en el mtodo. Al
declarar una variable local como final, se impide que se le asigne un valor en ms
de una ocasin.
La clase Object
Java define la clase especial Obj ect que es una superclase implcita de todas las
dems clases. Es decir, todas las clases son subclases de Obj ect, lo que significa que
una variable de referencia de tipo Obj ect puede hacer referencia a un objeto de cual-
quier clase. Adems, como las matrices se implementan como clases, una variable de
tipo Obj ect tambin puede hacer referencia a cualquier matriz.
Obj ect define los mtodos mostrados en la tabla 7.1, de modo que todos estn dispo-
nibles en todos los objetos.
Tabla 7 .1. Mtodos definidos por Object.
Mtodo
Obj ect clone ()
boolean equals (Object objeto)
void finalize ()
Class<?> getClass ()
Funcin
Crea un nuevo objeto idntico al clonado.
Determina si un objeto es igual a otro.
Se invoca antes de reciclar un objeto sin usar.
Obtiene la clase de un objeto en tiempo de ejecu-
cin.
Mtodo
i nt has hCo de ()
voi d no tify ()
void not ifyAll ()
String toStri ng ()
void wait ()
void wa it (long milise gundos )
void wait (long milisegundos,
int nanosegundos
Java 7 fJ41
Funcin
Devuelve el cdigo hash asociado al objeto invo-
cador.
Reanuda la ejecucin de un subproceso que espera
al objeto invocador.
Reanuda la ejecucin de todos los subprocesos que
esperan al objeto invocador.
Devuelve una cadena que describe el objeto.
Espera en otro subproceso de ejecucin.
Los mtodos getClass (), not i fy (), notifyAll () y wai t () se declaran como
final. Puede reemplazar el resto. Ms adelante veremos algunos de estos mtodos.
Pero fjese en dos de ellos: equals () y toString (). e quals () compara dos objetos.
Devuelve true si los objetos son equivalentes y false en caso contrario. El mtodo
t o String () devuelve una cadena que contiene una descripcin del objeto en el que se
invoca. Adems, este mtodo se invoca automticamente al representar un objeto por
medio de println ().Muchas clases reemplazan este mtodo, ya que al hacerlo pueden
definir una descripcin concreta de los tipos de objetos que crean.
Para terminar, fjese en la sintaxis del tipo que devuelve getClass ().Est relacio-
nado con la funcin de genricos de Java. Los genricos permiten que el tipo de datos
usado por una clase o un mtodo se especifique como parmetro, como veremos en el
captulo 13.
Evaluacin de conocimientos
l. Una superclase tiene acceso a los miembros de una subclase? Una subclase tiene
acceso a los miembros de una superclase?
2. Cree una subclase de TwoDShape con el nombre Circ l e . Incluya un mtodo
a r ea () que calcule el rea del crculo y un constructor que use super para inicia-
lizar la parte Two DShape.
3. Cmo se evita que una subclase acceda a un miembro de la superclase?
4. Describa la funcin y el uso de ambas versiones de sup er.
5. Dada la siguiente jerarqua:
class Alpha { .. .
class Beta extends Alpha
Class Garruna extends Beta
f .J:lel 7. Herencia
En qu orden se invocan los constructores de estas clases al crear una instanru
de un objeto Gamma?
6. Una referencia de superclase puede hacer referencia a un objeto de subclase
Explique la importancia de esta afirmacin con respecto al reemplazo dt
mtodos.
7. Qu es una clase abstracta?
8. Cmo se impide que un mtodo se reemplace? Cmo se impide que se heredE
una clase?
9. Explique cmo se usa la herencia, el reemplazo de mtodos y las clases abstractru
para admitir el polimorfismo.
10. Qu clase es una superclase de todas las dems clases?
11. Una clase que contiene al menos un mtodo abstracto debe, por s misma, decla-
rarse como abstracta. Verdadero o falso?
12. Qu palabra clave se usa para crear una constante con nombre?

S-ar putea să vă placă și