Вы находитесь на странице: 1из 21

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces

1/21
Herencia e Interfaces
Herencia
Introduccin
En C# cualquier dato es un objeto porque todos los tipos derivan implcitamente de este
tipo, y heredan los mtodos y campos definidos en dicha clase. Cada nuevo tipo tiene
todo lo que tiene el tipo obj ect y puede utilizarlo e incluso redefinirlo- y aade una
serie de campos y mtodos.
Cuando, por ejemplo, se est diseando una interfaz grfica, sera absurdo tener que
escribir todo el cdigo para cada ventana cuando muchas de ellas son casi iguales y
realizan tareas parecidas. Lo lgico es aprovechar ese cdigo que ya est escrito y
probado y cambiar y aadir lo que sea necesario para ir obteniendo nuevas ventanas
particularizadas. Piense, por ejemplo, en un cuadro de dilogo comn. Resulta
coherente escribir un cdigo bastante general que sirva para cualquier ventana y
posteriormente modificarlo que no sea redimensionable, que tenga un ttulo diferente,
etc-, aadiendo caractersticas nuevos botones, etc- o cambiando algunas de ellas.

La herencia proporciona un mecanismo para definir una nueva clase, a partir de otra
que ya existe, modificndola. La nueva clase que se define, se denomina clase derivada
y la clase de la que se hereda se llama clase base.

La clase derivada es la misma clase base a la que se aaden nuevos miembros (campos,
mtodos, etc) y/o se redefinen alguno de ellos.

La clase base puede ser a su vez, clase derivada de otra. Cuando hay muchas clases
relacionadas entre s por el mecanismo de la herencia, se habla de jerarqua de clases.

La herencia proporciona dos grandes ventajas al programador: por un lado, permite la
reutilizacin de cdigo y por otro permite el polimorfismo de referencias.
Fundamentos de la herencia
Para indicar que una clase hereda de otra, se utiliza el siguiente formato general:

[ modi f i cador ] cl ass Cl aseDer i vada : Cl aseBase
{
/ / Cuer po de l a cl ase
}

La clase derivada hereda todos los miembros de la clase base, es decir, tiene todos y
cada uno miembros de la clase base y los que el programador desee aadir.

Ejemplo:

usi ng Syst em;

namespace Her enci a
{
publ i c cl ass Cl aseBase
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
2/21
{
publ i c i nt a;
publ i c i nt b;
publ i c voi d I mpr i mi r _ab( )
{
Consol e. Wr i t eLi ne( " a={0}, b={1}" , a, b) ;
}
}

publ i c cl ass Cl aseDer i vada: Cl aseBase
{
publ i c i nt c;
publ i c voi d I mpr i mi r _c( )
{
Consol e. Wr i t eLi ne( " c={0}" , c) ;
}
publ i c voi d I mpr i mi r Suma( )
{
Consol e. Wr i t eLi ne( " Suma={0}" , a+b+c) ;
}
}

cl ass Her enci aApp
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
/ / Se cr ean obj et os de l as cl ases base y der i vada
Cl aseBase cl aseBase=new Cl aseBase( ) ;
Cl aseDer i vada cl aseDer i vada=new Cl aseDer i vada( ) ;

/ / La cl ase base puede i nvocar sus mi embr os desde Mai n
cl aseBase. a=11;
cl aseBase. b=12;

/ / se i mpr i men l os campos de l a cl ase base
cl aseBase. I mpr i mi r _ab( ) ;

/ / La cl ase Der i vada t i ene como mi embr os pr opi os
/ / t odos l os de l a cl ase base
cl aseDer i vada. a=25;
cl aseDer i vada. b=26;
cl aseDer i vada. c=27;
cl aseDer i vada. I mpr i mi r _ab( ) ;

/ / y l os aadi dos por el pr ogr amador
cl aseDer i vada. I mpr i mi r _c( ) ;
cl aseDer i vada. I mpr i mi r Suma( ) ;
}
}
}

La salida de este programa es:

a=11, b=12
a=25, b=26
c=27
Suma=78

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
3/21
En este ejemplo se observa que la Cl aseDer i vada tiene como miembros todos los
miembros que ha definido como propios y todos los miembros que tiene la clase de la
que deriva.

Es decir, su miembros son:

Campos: a, b y c
Mtodos: I mpr i mi r _ab( ) , I mpr i mi r _c( ) , I mpr i mi r Suma( )

Por eso, el objeto cl aseDer i vada puede acceder a los campos a y b e invocar el
mtodo I mpr i mi r _ab( ) y puede llamar desde un mtodo propio como es
I mpr i mi r Suma( ) a los campos a y b directamente.

En este caso, se dice que la clase Cl aseDer i vada deriva de la clase Cl aseBase o que la
clase Cl aseBase es una superclase (clase padre) de la Cl aseDer i vada -(clase hija o
subclase).

Control de acceso a los miembros de la clase base
Los modificadores de acceso de los miembros de la superclase y de la subclase definen
el encapsulamiento y el interfaz de la clase.

La forma de acceso a cada uno de los miembros heredados de la clase derivada, depende
de los modificadores de acceso que cada uno de dichos miembros tenga en la clase base.

En el primer ejemplo se ha considerado que todos los miembros fueran pblicos para
explicar directamente la herencia. En general, esto no es una buena prctica de
programacin. Los datos de una clase deben estar protegidos. Se ha estudiado con
anterioridad cmo es el acceso a los miembros de una clase, dependiendo de cmo est
definido cada uno de los miembros publ i c o pr i vat e-.

El problema que aparece con la herencia es el control de acceso a los miembros de la
clase base por parte de la clase derivada. Dicho de otro modo: puede un mtodo de la
clase derivada acceder a miembros privados de la clase base? Como anteriormente se ha
explicado, no se puede acceder a campos privados desde ninguna clase. Sin embargo,
sera muy conveniente que desde la clase derivada se pudiera llamar a los miembros
privados de la clase base ya que stos son heredados. Esto es posible con el modificador
de acceso pr ot ect ed. Un dato precedido por este modificador es pblico para
cualquier clase derivada pero privado para el resto de las clases.

Es acceso a los datos se puede resumir esquemticamente en estos tres puntos:

a) Un miembro de la clase base declarado como publ i c, pasa a ser un miembro
publ i c en la clase derivada, es decir, puede ser utilizado sin ningn tipo de
restriccin por la clase derivada, tanto en su propio cdigo como desde el
exterior de la clase que la ha definido.
b) Un miembro declarado como pr i vat e en la clase base, aunque pertenece a la
clase derivada, no puede ser llamado ni desde el propio cdigo de la clase
derivada ni, por supuesto, desde ningn cdigo exterior a su clase. Dicho de otro
modo: aunque una clase derivada incluye todos los miembros de la clase base de
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
4/21
la que deriva, no puede acceder a aquellos miembros que han sido declarados
como pr i vat e en la clase base.
c) Un miembro declarado como pr ot ect ed, puede ser llamado desde el interior del
cdigo de la clase derivada pero no desde el exterior de dicha clase, es decir, los
objetos de la clase derivada no pueden acceder a ellos. Un miembro pr ot ect ed
es en realidad un miembro pr i vat e para cualquier clase excepto para una clase
derivada, que es publ i c, con la excepcin antes aludida.



El siguiente ejemplo sirve para ilustrar lo anterior:

usi ng Syst em;

namespace AccesoConHer enci a
{
publ i c cl ass Base
{
pr i vat e i nt xPr i ;
publ i c i nt yPub;
pr ot ect ed i nt zPr o;

publ i c voi d I mpr i mi r ( )
{
/ / est e met odo puede l l amar a t odos l os mi embr os
/ / publ i c y pr i vat e desde l a pr opi a cl ase
Consol e. Wr i t eLi ne( " xPr i ={0}, yPub={1}, zPr o={2}" , xPr i ,
yPub, zPr o) ;
}
}
publ i c cl ass Der i vada : Base
{
i nt x;
publ i c voi d Suma( )
{
/ / Est e mt odo no puede ut i l i zar xPr i ,
/ / por ser pr i vado en l a cl ase base
Consol e. Wr i t eLi ne( " Suma={0}" , yPub+zPr o+x) ;
}
}
cl ass Apl i caci on
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
Base unaCl aseBase=new Base( ) ;

/ / ERROR. Mi embr o pr i vat
/ / unaCl aseBase. xPr i =2;

unaCl aseBase. yPub=3;

/ / ERROR. Mi embr o pr ot ect ed
/ / unaCl aseBase. zPr o=4;

Der i vada unaCl aseDer i vada=new Der i vada( ) ;

/ / ERROR. Mi embr o pr i vat e
/ / unaCl aseDer i vada. xPr i =12;

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
5/21

unaCl aseDer i vada. yPub=13;

/ / ERROR. Es mi embr o pr ot ect ed
/ / unaCl aseDer i vada. zPr o=14;

/ / ERROR. Es mi embr o pr i vat e
/ / unaCl aseDer i vada. x=15;


unaCl aseBase. I mpr i mi r ( ) ;
unaCl aseDer i vada. I mpr i mi r ( ) ;
unaCl aseDer i vada. Suma( ) ;

}
}

}

La salida de este programa es:

xPr i =0; yPub=3, zPr o=0
xPr i =0; yPub=13, zPr o=0
Suma=13


La herencia es muy importante en la programacin orientada a objetos. C# proporciona
una serie de libreras que es muy importante conocer para poder aprovecharla en su
integridad. En la totalidad de los programas grficos, el programador aprovecha ese
cdigo ya optimizado y probado por Microsoft heredando de algunas clases para con
algunas modificaciones y aadidos escribir su cdigo.

Sobrescritura
Muchas veces resulta muy til y dota a la programacin orientada a objetos de
una extraordinaria flexibilidad, poder redefinir sobrescribir- un mtodo o
propiedad de la clase base en la clase derivada.

En C#, el programador debe indicar qu mtodos de una clase pueden ser sobrescritos y
cules no. Para ello lo debe indicar por medio del modificador vi r t ual . En la clase
derivada se debe preceder el nombre del mtodo del modificador over r i de.

No es posible cambiar la accesibilidad del mtodo en la clase derivada.

La palabra reservada base permite utilizar los mtodos de la clase base que estn
sobrescritos en la clase derivada.

Para explicar este concepto, se va a utilizar un ejemplo anterior, se va a sobrescribir el
mtodo I mpr i mi r ( ) :

usi ng Syst em;
namespace Consol eAppl i cat i on1
{
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
6/21
publ i c cl ass Cl aseBase
{
publ i c i nt a;
publ i c i nt b;
public virtual void Imprimir()
{
Consol e. Wr i t eLi ne( " a={0}, b={1}" , a, b) ;
}
}

publ i c cl ass Cl aseDer i vada: Cl aseBase
{
publ i c i nt c;
public override void Imprimir()
{
Consol e. Wr i t eLi ne( " a={0}, b={1}, c={2}" , a, b, c) ;
}
}
cl ass Her enci aApp
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
Cl aseBase cl aseBase=new Cl aseBase( ) ;
Cl aseDer i vada cl aseDer i vada=new Cl aseDer i vada( ) ;

cl aseBase. a=11;
cl aseBase. b=12;
cl aseBase. I mpr i mi r ( ) ;


cl aseDer i vada. a=25;
cl aseDer i vada. b=26;
cl aseDer i vada. c=27;
cl aseDer i vada. I mpr i mi r ( ) ;

}
}
}


La salida de este programa es:

a=11, b=12
a=25, b=26, c=27

En este sencillo ejemplo, se ha sobrescrito el mtodo I mpr i mi r ( ) . Para ello, en la clase
base se define I mpr i mi r ( ) precedido del modificador vi r t ual :

publ i c vi r t ual voi d I mpr i mi r ( )
En la clase derivada, el mtodo que se sobrescribe debe tener el mismo nombre e ir
precedido del modificador over r i de, que indica que en la clase base existe un mtodo
con el mismo nombre y con cdigo diferente.
publ i c over r i de voi d I mpr i mi r ( )
Dependiendo del objeto que lo llama, el compilador decide si utiliza el mtodo de la
clase base o el de la derivada.
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
7/21
Para invocar un mtodo de la clase base que est sobrescrito en la clase derivada se
utiliza la referencia base.
Por ejemplo, si desde la clase base se desea llamar al mtodo Imprimir() de la clase
base se invoca de la siguiente forma:
base. I mpr i mi r ( ) .

Por ejemplo, se podra haber definido el mtodo I mpr i mi r ( ) en la clase derivada de la
siguiente manera:

publ i c cl ass Cl aseDer i vada: Cl aseBase
{
publ i c i nt c;
publ i c over r i de voi d I mpr i mi r ( )
{
base. I mpr i mi r ( ) ;
Consol e. Wr i t eLi ne( " c={0}" , c) ;
}
}

El mismo cdigo del ejemplo anterior dara una salida del programa ligeramente
diferente:

a=11, b=12
a=25, b=26
c=27

Esto es as porque al ejecutar

base. I mpr i mi r ( ) ;

el compilador realiza un salto de lnea. Sin embargo, en el siguiente apartadose tratar
este concepto con ms detalle.



Constructores y herencia
Orden de ejecucin de los constructores
Cuando se invoca a un constructor de la clase derivada, se ejecuta previamente, por
defecto, el constructor de la clase base. Por ejemplo:

usi ng Syst em;

namespace Her enci a
{
publ i c cl ass Cl aseBase
{
i nt x, y;
publ i c Cl aseBase( )
{
Consol e. Wr i t eLi ne( " Const r uct or de l a cl ase base" ) ;
}
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
8/21

}
publ i c cl ass Cl aseDer i vada : Cl aseBase
{
i nt z;
publ i c Cl aseDer i vada( )
{
Consol e. Wr i t eLi ne( " Const r uct or de l a cl ase der i vada" ) ;
}
}
publ i c cl ass Apl i caci on
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
Cl aseDer i vada d=new Cl aseDer i vada( ) ;
}
}
}

La salida de este programa es la siguiente:

Const r uct or de l a cl ase base
Const r uct or de l a cl ase der i vada

Palabra reservada base
En ocasiones, puede ser interesante ejecutar el constructor de la clase base
proporcionndole algunos argumentos o ejecutar algn mtodo de la clase base desde el
cdigo de la clase derivada. Para ello se utiliza la palabra base.

base es muy parecido a t hi s. La diferencia ms importante es que la segunda se refiere
siempre al propio objeto, y base hace referencia a la superclase, a la clase de la que
deriva la clase actual, a la clase padre. base puede utilizarse de dos formas:

A) Para hacer referencia a alguno de los constructores de la clase base en la clase
derivada.

Como ejemplo, se va a calcular el rea de un cilindro. Se definen las clases Ci r cul o -
con dos campos, uno constante y que define PI , y el r adi o- y la clase Ci l i ndr o, que se
considera como un crculo al que se le ha aadido una al t ur a. El rea de un crculo es
el producto de por el cuadrado del radio, y el de un cilindro es el rea de la base que
es un crculo- por la altura. De esta manera, se tendr que sobrescribir el mtodo Ar ea( )
en la clase derivada.

usi ng Syst em;

namespace TemasMat emat i cos
{
publ i c cl ass Ci r cul o
{
publ i c const doubl e PI =3. 141592;
pr ot ect ed doubl e r adi o;
publ i c Ci r cul o( doubl e r adi o)
{
t hi s. r adi o=r adi o;
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
9/21
}
publ i c vi r t ual doubl e Ar ea( )
{
r et ur n PI *r adi o*r adi o;
}
}
publ i c cl ass Ci l i ndr o : Ci r cul o
{
doubl e al t ur a;
publ i c Ci l i ndr o( doubl e r adi o, doubl e al t ur a) : base( r adi o)
{
t hi s. al t ur a=al t ur a;
}
publ i c over r i de doubl e Ar ea( )
{
r et ur n PI *r adi o*r adi o*al t ur a;
}
}
cl ass Apl i caci on
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
Ci l i ndr o c=new Ci l i ndr o( 1, 2) ;
Consol e. Wr i t eLi ne( " Ar ea={0}" , c. Ar ea( ) ) ;
}
}
}

La salida de este programa es:

Ar ea=6. 283184

Este programa tiene alguna lneas en la que en importante detenerse.

a) publ i c vi r t ual doubl e Ar ea( )
En la clase base, Ci r cul o, se define el mtodo Ar ea( ) como vi r t ual , para
indicar que ser sobrescrito en la clase derivada.

b) publ i c over r i de doubl e Ar ea( )

En la clase Ci l i ndr o que deriva de la clase Ci r cul o, se sobrescribe este mtodo
para calcular el rea del cilindro ya que parece que se debera llamar de la
misma manera y se precede del modificador over r i de.

c) publ i c Ci l i ndr o( doubl e r adi o, doubl e al t ur a) : base( r adi o)
En esta sentencia se invoca en primer lugar el constructor de la clase base para
que inicialice el r adi o y despus se ejecuta el cdigo de la clase derivada.

En general, para ejecutar el constructor de la clase base, en la misma lnea donde
se define el constructor de la clase derivada, se sigue la palabra base con los
argumentos necesarios para que se ejecute un determinado constructor de la
superclase precedida de : .

Dependiendo del constructor de la clase base, se utilizan unos argumentos u
otros.
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
10/21

Tambin se puede llamar a un constructor de la misma clase, escribiendo
despus del constructor : t hi s( ) ,de la misma forma que se ha hecho con
: base( ) .

B) Para invocar un mtodo de la clase base desde la clase derivada se utiliza
cuando dicho mtodo est sobrecargado-.

Este concepto se ha explicado brevemente con anterioridad. Sin embargo, se aprovecha
este ejemplo para profundizar un poco ms en l.

Si se quiere invocar un mtodo de la clase base se puede invocar con la palabra
reservada base, de la misma forma que se utiliza para llamar al objeto actual con t hi s.

As, en el ejemplo anterior, si se quiere invocar el mtodo Ar ea( ) de la clase Ci r cul o,
desde el cdigo de la clase derivada se hace de la siguiente manera:

base. Ar ea( ) .

En ese mismo bloque de cdigo es decir, en la clase derivada- para referirse al mtodo
Ar ea( ) de la clase Ci l i ndr o, se puede hacer de dos formas:

Ar ea( )

o bien

t hi s. Ar ea( )

Por ejemplo,

publ i c cl ass Cl aseDer i vada : Cl aseBase
{
publ i c voi d Hacer Al goDeAl gunaManer a( )
{
base. Hacer Al go( ) ;
/ / r eal i zar el r est o del pr ocedi mi ent o
}
}

En el ejemplo se observa que con la lnea:

base. Hacer Al go( ) ;

se refiere al mtodo de la clase base, que tendr el mismo nombre que en la clase
derivada.

Polimorfismo de referencias

Se puede asignar a una referencia de una superclase una referencia de una subclase. Esta
es una caracterstica muy til y tremendamente potente de C#, que tambin se encuentra
en C++ y en Java, aunque con diferencias sustanciales.

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
11/21
El siguiente ejemplo sirve para explicar este concepto:

usi ng Syst em;

namespace Pol i mor f i smo
{
publ i c cl ass Caj a
{
pr ot ect ed doubl e x;
pr ot ect ed doubl e y;
pr ot ect ed doubl e z;

publ i c Caj a( doubl e ancho, doubl e l ar go, doubl e al t o)
{
x=ancho;
y=l ar go;
z=al t o;
}
publ i c doubl e Vol umen( )
{
r et ur n x*y*z;
}
}
publ i c cl ass Caj aConPeso : Caj a
{
publ i c doubl e Peso;
public CajaConPeso(double a, double b, double c,
double peso) : base(a,b,c)
{
t hi s. Peso=peso;
}
}

publ i c cl ass Mi Cl aseApp
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
/ / Se obt i enen r ef er enci as a obj et os
/ / de l as cl ases base y der i vada
Caj a r ef Caj a=new Caj a( 1, 2, 3) ;
Caj aConPeso r ef Caj aConPeso=new Caj aConPeso( 3, 4, 5, 6) ;

doubl e vol umen;
vol umen=r ef Caj a. Vol umen( ) ;
Consol e. Wr i t eLi ne( " Vol ={0}" , vol umen) ;

/ / se asi gna una r ef er enci a de l a cl ase base
/ / a una r ef enci a. de l a cl ase der i vada
r ef Caj a=r ef Caj aConPeso;

vol umen=r ef Caj a. Vol umen( ) ;
Consol e. Wr i t eLi ne( " Vol ={0}" , vol umen) ;
/ / Consol e. Wr i t eLi ne( " Peso={0}" , r ef Caj a. Peso) ;

}
}
}

La salida de este programa es:

Vol =6
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
12/21
Vol =60


r ef Caj aConPeso es una referencia a un objeto de la clase Caj aConPeso clase
derivada- y r ef Caj a una referencia a un objeto de la clase Caj a clase base-. Como
Caj aConPeso es una subclase de Caj a es posible asignar a r ef Caj a la referencia
r ef Caj aConPeso.

Es muy importante entender bien que el que determina qu miembros son accesibles y
cules no es el tipo de variable de referencia ni el tipo de objeto al que se refiere.
Cuando una referencia a un objeto de una subclase se asigna a una referencia de la
superclase, slo se tiene acceso a aqullas partes del objeto definidas en la superclase
(figura 6.1).

Por eso, aunque ahora r ef Caj a y r ef Caj aConPeso referencian al mismo objeto de la
clase Caj aConPeso, r ef Caj a no puede acceder al campo Peso a pesar de ser una
referencia a un objeto de la clase Caj aConPeso. Esto tiene sentido ya que la clase base
la superclase- no tiene conocimiento no puede ver y por lo tanto tampoco acceder o
invocar- a lo que en la clase derivada se aade a su propia definicin. No es posible para
una referencia de tipo Caj a acceder al campo Peso ya que no ha sido definido en la
clase base Caj a. Por eso, la ltima lnea de este programa est comentada, porque dara
un error al compilar.

Se podra haber creado esa referencia directamente, al crear un objeto de la clase
derivada, es decir,

Caj a r ef Caj a=new Caj aConPeso( 3, 4, 5, 6) ;

En este caso, r ef Caj a es una referencia de tipo Caj a a un objeto de tipo Caj aConPeso.

Esto es extraordinariamente importante y dota de una gran flexibilidad y potencia a este
lenguaje, ya que si una superclase contiene un mtodo que est sobrescrito en la
subclase, entonces, cuando se referencian distintos tipos de objetos a travs de una
variable de referencia de la superclase, se ejecutan distintas versiones del mtodo.














Figura 6.1: La referencia r ef Caj a slo puede invocar los miembros de la clase
derivada que conoce, que son los que tiene en la superclase.

Objeto tipo CajaConPeso
Objeto tipo Caja
r ef Caj a
r ef Caj aCoPeso
Peso
x
y
z
Vol umen( )
x
y
z
Vol umen( )
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
13/21


por ejemplo: suponga que se desea definir un mtodo que puede admitir como
parmetro cualquier tipo de dato. Dicho mtodo podra tener la siguiente forma:

publ i c voi d UnMet odo( obj ect obj )
{
/ / codi go
}

Como hasta ahora se ha venido repitiendo, todo dato en C# es un objeto, deriva de l y
por lo tanto puede tratarse como tal. El polimorfismo de referencias nos permite escribir
el siguiente fragmento de cdigo:

st r i ng unSt r i ng = Hol a Mundo;
i nt unEnt er o = 3;
const doubl e PI = 3. 14;
Mi Cl ase unaCl ase = new Mi Cl ase( ) ;
Cl aseDeMi cr osof t ot r aCl ase = new Cl aseDeMi cr osof t ( ) ;
UnMet odo( unSt r i ng) ;
UnMet odo( unEnt er o) ;
UnMet odo( PI ) ;
UnMet odo( unaCl ase) ;
UnMet odo( ot r aCl ase) ;

Observe que no se ha sobrecargado el mtodo UnMet odo. El cdigo anterior es posible
por el polimorfismo de referencias, porque realmente, ocurre lo siguiente:

obj ect r ef Obj ; / / I ni ci al ment e nul l , no r ef er enci a a nada
st r i ng unSt r i ng = Hol a Mundo;
r ef Obj = unSt r i ng;
UnMet odo( r ef Obj ) ;
. . . .

La referencia al st r i ng unSt r i ng, apunta a un objeto que es la cadena Hol a Mundo,
que como todo st r i ng es un objeto, es decir, deriva de obj ect . Por el polimorfismo de
referencias, es posible asignar una referencia de una clase en este caso un st r i ng- a
una referencia de una superclase en este caso a obj ect - . Esta tcnica permite tratar
los datos de manera genrica.

El polimorfismo de referencias existe tambin en Java pero la tcnica para conseguirlo
es menos directa y complicada que en C#, ya que en Java no todos los tipos de datos son
objetos. En concreto, no lo son los llamados tipos simples, aunque Java proporciona a
todos ellos una clase intermedia llamada envoltorio que los convierte en clases. Pero
siempre es necesario realizar un paso intermedio para convertir los llamados tipos
bsicos a tipos de su clase envoltorio. En C# no es necesario realizar este paso,
porque los tipos de datos son objetos. Esta manera de trabajar proporciona una enorme
potencia a este lenguaje.


Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
14/21
Interfaces.

Una i nt er f ace garantiza un determinado comportamiento de una clase o estructura.
Cuando una clase implementa una i nt er f ace se garantiza que soportar los mtodos,
propiedades, eventos e indexadores de la i nt er f ace. La i nt er f ace es una alternativa a
una clase abstracta.

Una i nt er f ace es similar a una clase o a una estructura, pero sus miembros son
abstractos, es decir, no estn definidos, no tienen cdigo. Declara modos de
comportamiento, pero deja su definicin para las clases que la implementen.

Cuando una clase implementa una i nt er f ace, debe implementar todos los mtodos de
la i nt er f ace.

Hay una gran diferencia entre heredar de una clase abstracta e implementar una
interface. Por ejemplo: un Coche es un vehculo hereda las caractersticas y
comportamiento de un vehculo-, pero puede tener la capacidad de
Poder Regul ar LaTemper at ur a (como una casa, por ejemplo). Cuando se hereda, se
hace referencia a lo que se es, y cuando se implementa una i nt er f ace se hace
referencia a la capacidad de comportarse de una determinada manera.

En este captulo se estudia cmo crear, implementar y usar las interfaces. Adems, se
tratar cmo implementar mltiples interfaces.

Estructura de una interface.
La estructura de una i nt er f ace es la siguiente:

[atributos
OPC
] [modificadores de acceso
OPC
] i nt er f ace NombreDeLaInterface [:interfaces-
base]
{
/ / Cuer po de l a i nt er f ace
}

Los atributos se tratarn ms adelante. Por ahora es suficiente con decir que los
atributos en C# contienen informacin sobre el tipo de dato y puede ser consultada
mediante reflexin. Los atributos son opcionales.

Los modificadores de acceso opcionales- pueden ser los mismos que los de las clases y
con el mismo efecto, a excepcin de abst r act y seal ed, que no tienen sentido para
una i nt er f ace, es decir: new, publ i c, pr ot ect ed, i nt er nal y pr i vat e.

La palabra i nt er f ace es seguida del nombre de la i nt er f ace. Generalmente aunque
no hay porqu hacerlo as- se suele utilizar -como convenio- un nombre que comience
por la letra mayscula I , como I Cl onabl e, I Mi I nt er f ace, etc...

Si la i nt er f ace hereda a su vez de otras interfaces, entonces, despus del nombre de la
i nt er f ace se escribe dos puntos (: ) y, separados por comas, los nombres de las
interfaces que implementa.
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
15/21

Por ejemplo:

i nt er f ace I Avi on : I Vol ar , I Vehi cul o
{
/ / Cdi go
}

Suponga que desea crear una interface que defina la capacidad de ser imprimible que
se llame I I mpr i mi bl e y que tenga un mtodo que se llame I mpr i mi r ( )

La definicin sera:

i nt er f ace I I mpr i mi bl e
{
voi d I mpr i mi r ( ) ;
}

Por ejemplo, se puede crear una clase Document o. Para indicar que el tipo Document o se
puede imprimir basta con que implemente la i nt er f ace I I mpr i mi bl e. Implementar
una i nt er f ce no es ms que escribir cdigo en los mtodos definidos en la i nt er f ace.
La sintaxis es la misma que si derivara de la i nt er f ace, aunque, como se ha dicho, es
necesario implementar todos los mtodos de la i nt er f ace. Por ejemplo:

publ i c i nt er f ace I I mpr i mi bl e
{
voi d I mpr i mi r ( ) ;
}

publ i c cl ass Document o : I I mpr i mi bl e
{
st r i ng cont eni do;
publ i c Document o( st r i ng f r ase)
{
cont eni do=f r ase;
}
publ i c voi d I mpr i mi r ( )
{
Consol e. Wr i t eLi ne( cont eni do) ;
}
}

cl ass Mi Apl i caci on
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
Document o unDocument o=new Document o( " Cont eni do 1" ) ;
unDocument o. I mpr i mi r ( ) ;
}
}

La salida de este programa es:

Cont eni do 1


Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
16/21
Suponga que se desea crear otra i nt er f ace que defina el comportamiento necesario
para guardar y leer desde una fuente de datos una base de datos, o el disco duro, ...-. Se
le llamar, por ejemplo, I Ar chi vabl e. Esta i nt er f ace podra tener dos mtodos:
Leer ( ) y Escr i bi r ( ) .

Una posible definicin es, por ejemplo:

i nt er f ace I Ar chi vabl e
{
voi d Leer ( ) ;
voi d Escr i bi r ( )
}

Para indicar que la clase Document o tiene la capacidad de ser almacenado en el disco
duro, basta con que implemente la i nt er f ace I Ar chi vabl e e I I mpr i mi bl e.

publ i c cl ass Document o: I I mpr i mi bl e, I Ar chi vabl e
{
voi d Leer ( )
{
/ / Cdi go que i mpl ement a el mt odo
}
voi d Escr i bi r ( )
{
/ / Cdi go que i mpl ement a el mt odo
}
publ i c voi d I mpr i mi r ( )
{
/ / Cdi go que i mpl ement a el mt odo
}
/ / Ot r os mi embr os y cdi go pr opi o de l a cl ase Document o
}

Es responsabilidad del autor de la clase Document o implementar o definir el
comportamiento de los mtodos de la i nt er f ace. Si una clase implementa una
i nt er f ace debe implementar de manera obligatoria todos los mtodos de la
i nt er f ace.

Todos los miembros de una i nt er f ace son pblicos por defecto- para que puedan ser
implementados por otras clase. Por esta razn no llevan ningn modificador.

A continuacin se escribe un ejemplo completo. Se define una nueva clase,
Rect ngul o, que implemente la i nt er f ace I I mpr i mi bl e. Se puede observar cmo se
definen de manera diferente el mismo mtodo I mpr i mi r ( ) en las dos clases y cmo el
Rect ngul o no tiene la capacidad de ser almacenado en disco.

El ejemplo completo sera:


i nt er f ace I Ar chi vabl e
{
voi d Leer ( ) ;
voi d Escr i bi r ( ) ;
}

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
17/21
publ i c i nt er f ace I I mpr i mi bl e
{
voi d I mpr i mi r ( ) ;
}

publ i c cl ass Rect angul o : I I mpr i mi bl e
{
i nt ancho;
i nt al t o;
publ i c Rect angul o( i nt l ado1, i nt l ado2)
{
ancho=l ado1;
al t o=l ado2;
}
publ i c voi d I mpr i mi r ( )
{
Consol e. Wr i t eLi ne( " ancho={0}, al t o={1}" , ancho, al t o) ;
}
}

publ i c cl ass Document o : I I mpr i mi bl e, I Ar chi vabl e
{
st r i ng cont eni do;
publ i c Document o( st r i ng f r ase)
{
cont eni do=f r ase;
}
publ i c voi d I mpr i mi r ( )
{
Consol e. Wr i t eLi ne( cont eni do) ;
}
publ i c voi d Leer ( )
{
Consol e. Wr i t eLi ne( " Leyendo CONTENI DO desde l e di sco dur o" ) ;
}
publ i c voi d Escr i bi r ( )
{
Consol e. Wr i t eLi ne( " Escr i bi endo CONTENI DO en di sco dur o" ) ;
}
}
cl ass Mi Apl i caci on
{
st at i c voi d Mai n( st r i ng[ ] ar gs)
{
Document o unDocument o=new Document o( " Cont eni do 1" ) ;
Rect ngul o unRect = new Rect ngul o( 12, 13) ;
unDocument o. Escr i bi r ( ) ;
unDocument o. Leer ( ) ;
unDocument o. I mpr i mi r ( ) ;
unRect . Escr i bi r ( ) ;

}
}
Polimorfismo de referencias

Se vuelve a estudiar aqu el polimorfismo de referencias, pero ahora desde un punto de
vista distinto: la i nt er f ace.


Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
18/21
No es posible crear objetos de tipo interface. Esto es lgico si pensamos que sus
mtodos estn declarados, pero no definidos. No tendra sentido un objeto sin mtodos
implementados.

Por esta razn, no se puede escribir:

I I mpr i mi bl e r ef I nt er f ace = new I I mpr i mi bl e( ) ;

Sin embargo, puede crearse una referencia de tipo interface para que apunte a
cualquier objeto de un tipo que implemente dicha i nt er f ace. Por ejemplo:

I I mpr i mi bl e r ef I nt er f ace;
Document o unDocument o=new Document o( t ext o del document o) ;
r ef I nt er f ace = ( I I mpr i mi bl e) unDocument o;

o bien, de manera ms comprimida:

I I mpr i mi bl e r ef I nt er f ace=( I I mpr i mi bl e) new Document o( ot r o t ext o ) ;

Se tiene la garanta de que cualquier clase que implemente la i nt er f ace I I mpr i mi bl e
tendr implementado el mtodo I mpr i mi r ( ) . Por eso, tiene sentido que una referencia a
una i nt er f ace pueda invocar todos los mtodos -que tenga declarados como
i nt er f ace- del objeto al que apunta. Sin embargo, la i nt er f ace I I mpr i mi bl e no
conoce cmo cada clase lo implementa ni le hace falta saberlo- ni qu ms miembros
tienen dichas clases. Por eso no puede invocar aquellos mtodos, propiedades, etc, que
sean nicamente propios del objeto.

Para ilustrar esta idea, piense en la aplicacin anterior con el siguiente cdigo en el
mtodo Mai n( ) :

Document o doc=new Document o( " cont eni do del document o" ) ;
I I mpr i mi bl e r ef I nt er f aceI mpr i mi bl e=doc;
r ef I nt er f aceI mpr i mi bl e. I mpr i mi r ( ) ;

o bien:

I I mpr i mi bl e r ef I nt er f aceI mpr i mi bl e;
r ef I nt er f aceI mpr i mi bl e=new Document o( " Una f r ase" ) ;
r ef I nt er f aceI mpr i mi bl e. I mpr i mi r ( ) ;



En resumen: una referencia a una determinada i nt er f ace, puede apuntar a cualquier
objeto de una clase o estructura que la implemente. Esto convierte el polimorfismo de
referencias en una herramienta extremadamente flexible y poderosa.

En el ejemplo anterior la clase Rect angul o implementa la i nt er f ace I I mpr i mi bl e y
la clase Document o las interfaces I I mpr i mi bl e e I Ar chi vabl e. Gracias al
polimorfismo de referrencias se puede escribir:

Document o doc=new Document o( " cont eni do del document o" ) ;
Rect angul o r ect =new Rect angul o( 12, 10) ;
I I mpr i mi bl e r ef I nt er f aceI mpr i mi bl e=doc;
Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
19/21
r ef I nt er f aceI mpr i mi bl e. I mpr i mi r ( ) ;
r ef I nt er f aceI mpr i mi bl e=r ect ;
r ef I nt er f aceI mpr i mi bl e. I mpr i mi r ( ) ;

o bien:

I I mpr i mi bl e r ef I nt er f aceI mpr i mi bl e;
r ef I nt er f aceI mpr i mi bl e=new Document o( " Una f r ase" ) ;
r ef I nt er f aceI mpr i mi bl e. I mpr i mi r ( ) ;
r ef I nt er f aceI mpr i mi bl e=new Rect angul o( 12, 10) ;
r ef I nt er f aceI mpr i mi bl e. I mpr i mi r ( ) ;


Miembros de interface.
Los miembros de una i nt er f ace son:

- Los que hereda de las interfaces base.
- Los que se declaran en la propia i nt er f ace.

Una i nt er f ace puede declarar cero o ms miembros, los cuales tienen acceso publ i c
por defecto. Por lo tanto, los miembros de una i nt er f ace no pueden ser declarados con
los modificadores abst r act , publ i c, pr ot ect ed, i nt er nal , pr i vat e, vi r t ual ,
over r i de, o st at i c.

Herencia de interfaces.
Una i nt er f ace puede heredar cero o ms interfaces de modo explcito (herencia
mltiple de interfaces). A tales interfaces se les llama interfaces base explcitas. No
obstante, una i nt er f ace no slo hereda las i nt er f ace que explcitamente indica, sino
tambin aqullas heredadas implcitamente, es decir, aqullas que han heredado las
i nt er f ace de las que hereda.

Por ejemplo:

i nt er f ace I Cont r ol
{
voi d Pai nt ( ) ;
}
/ / I Text Box her eda de I Cont r ol
i nt er f ace I Text Box: I Cont r ol
{
voi d Set Text ( st r i ng t ext ) ;
}

/ / I Li st Box her eda de I Cont r ol

i nt er f ace I Li st Box: I Cont r ol
{
voi d Set I t ems( st r i ng[ ] i t ems) ;
}
i nt er f ace I ComboBox: I Text Box, I Li st Box
{
/ / . . . .
}

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
20/21
La i nt er f ace I ComboBox tendr todos los mtodos de las interfaces I Text Box y de
I Li st Box, y por lo tanto, los de I Cont r ol , ya que implcitamente la hereda.

Implementacin de una interface.
Las interfaces pueden ser implementadas mediante clases y estructuras. Para indicar que
una clase o estructura implementa una i nt er f ace se ha de incluir el identificador de la
i nt er f ace en la lista de tipos base de la clase o estructura.


Ejemplo:

i nt er f ace I Cont r ol
{
voi d Pai nt ( ) ;
}
i nt er f ace I Text Box: I Cont r ol
{
voi d Set Text ( st r i ng t ext ) ;
}
cl ass Text Box: I Text Box
{
publ i c voi d Pai nt ( ) {. . . }
publ i c voi d Set Text ( st r i ng t ext o) {}
}

En este caso la clase Text Box no slo implementa I Text Box, sino tambin I Cont r ol .


Utilizacin de los operadores is y as con interfaces
Este operador se utiliza para averiguar si un determinado objeto soporta una i nt er f ace.

Su formato general es:

objeto i s tipo

La expresin anterior devuelve Tr ue en caso de que objeto soporte o implementa la
i nt er f ace tipo. En caso contrario devuelve Fal se.

Por ejemplo:

Rect angul o r ect = new Rect angul o( 13, 15) ;
i f ( r ect i s I I mpr i mi bl e )
{
I I mpr i mi bl e r ef I nt er f ace = ( I I mpr i mi bl e) r ect ;
r ef I nt er f ace. I mpr i mi r ( ) ;
}
. . . .

Pero el uso de i s es ineficaz porque puede generar excepciones. Una solucin mejor es
utilizar el operador as.

Marco Besteiro y Miguel Rodrguez Herencia e Interfaces
21/21
Este operador combina el operador i s y un casting o conversin de tipos. Primero se
chequea si la conversin es vlida y si es as, se realiza la conversin. En caso contrario,
se devuelve nul l . La expresin general es:

objeto as tipo

Por ejemplo:

Rect angul o r ect = new Rect angul o( 13, 15) ;
I I mpr i mi bl e r ef I nt er f ace = r ect as I I mpr i mi bl e;
i f ( r ef I nt er f ace ! = nul l )
r ef I nt er f ace. I mpr i mi r ( ) ;
el se
Consol e. Wr i t eLi ne( Rect angul o no sopor t a I i mpr i mi bl e)
. . . .

Вам также может понравиться