|
L'herència |
|
|
|
Avantatges de l'herència |
|
|
 |
Quan Java va néixer, els dissenyadors
de llenguatges buscaven solucions per a fer la programació més
eficaç, més ràpida i més barata. L'antecedent
eren els llenguatges procedurals com
ara C. En aquell context,amb la finalitat
d'aprofitar el codi de programes anteriors, es copiaven les llibreries
ja escrites i s'adaptaven les funcions
que contenien a les noves necessitats. Funcionava, malgrat ser poc pràctic.
Es va pensar en l'herència com
una estratègia per a facilitar la reutilització
de codi. A Java, s'aprofita el
codi creant classes noves, però
no sobre el buit, sinó sobre altres classes
que, en molts casos, no cal ni tocar perquè s'adaptin a les noves
necessitats. La idea és simple: tenim un objecte
que fa una tasca i necessitem una adaptació per a resoldre un problema
nou o diferent. No tenim la necessitat obligatòria de fer una classe
completament nova, sinó que estenem
la que tenim a una classe filla que incorpora
noves funcionalitats o en reescrivim (override)
les de la classe mare.
L'herència no és tampoc
una solució màgica per a tot. De fet, exigeix al programador
una bona dosi de planificació abans de posar-se a escriure codi:
les virtuts de les classes es propaguen
a les classes filles, però també
els seus defectes. Un projecte Java amb
una planificació deficient o incorrecta pot convertir l'experiència
de codificació en un suplici. El consell és simple: repenseu-vos
sempre amb molta cura la forma i continguts que doneu a les vostres classes.
A partir d'aquesta pràctica aprendrem a manipular algunes de les
possibilitats de l'herència en Java.
Aprendrem a crear classes filles que
seran capaces d'ampliar o modificar
les capacitats de les classes mares i
els donarem plasticitat a través de la sobrecàrrega
de mètodes i de constructors.
|
|
|
|
Superclasses i Subclasses: |
|
|
|
La definició de l'herència és molt simple. Si escric
una classe nova que és descendent d'una classe que ja existeix
només he de dir:
class Nova extends
Vella {
...
} |
Els membres no privats de la classe
mare passen a ser automàticament membres
de la classe filla. La classe mare
és la superclasse i la filla és
una subclasse.
|
|
|
|
Imaginem un centre escolar com un edifici que
agrupa diferents categories d'espais físics. Una expressió
simplificada d'aquesta realitat seria aquesta: |
|
|
|
Qualsevol espai del centre és un "objecte"
Espai, però no tots els espais es descriuen
de la mateixa forma: la cafeteria té
peculiaritats que no té una aula genèrica
o el despatx d'un departament. El gràfic
interpreta de forma jeràrquica les categories d'espais;disposem
d'aules i d'espais de treball dels professors. Les aules poden ser un
gimnàs, aules d'informàtica o laboratoris.
Aquesta estructura d'arbre es pot traduir fàcilment a diferents
classes vinculades per herència,
on la superclasse de totes és
"Espai". Un espai qualsevol té
un codi que l'identifica, una superfície, disposa o no de llum
natural, té un consum elèctric particular, etc. Un despatx
a més, té taules per als professors o màquina de
cafè. Aquest instrument no està instal·lat a les
aules, les quals, en canvi, tenen un aforament màxim o disposen
o no de cadires de pala.
Si codifiquem tot això podria quedar així: escriviu cadascuna
d'aquestes classes en el mateix fitxer:
|
|
|
 |
class Espai {
String codi;
int metresquadrats;
boolean llumnatural;
int consumelectric;
public Espai() {
}
public Espai(String codi, int metres, boolean
llum, int consum) {
this.codi = codi;
this.metresquadrats=metres;
this.llumnatural=llum;
this.consumelectric=consum;
}
public double consumM2() {
if (metresquadrats>0)
{ return consumelectric/metresquadrats; }
else { return
0; }
}
}
|
|
|
class Aula extends Espai {
boolean cadiresdepala;
boolean connectorsxarxa;
int places;
int ordinadors;
public Aula() {
}
public Aula(String codi, int metres, boolean
llum,
int consum, int places) {
super(codi,metres,llum,consum);
this.places=places;
}
public double espaiEstudiant() {
if
(places>0) {
return metresquadrats / places;
} else {
return 0;
}
}
public double espaiEstudiant(int m, int
p) {
this.places = p;
this.metresquadrats
= m;
if
(places>0) {
return metresquadrats / places;
} else {
return 0;
}
}
}
|
|
|
class Despatx extends Espai
{
int taules;
boolean maquinadecafe;
public Despatx() {
}
public Despatx(String codi, int metres,
boolean llum,int consum) {
super(codi,metres,llum,consum);
}
}
|
|
|
class Laboratori extends Aula {
int bunsens;
public Laboratori() {
}
public Laboratori(String codi, int metres,
boolean llum,
int
consum, int places) {
super(codi,metres,llum,consum,places);
}
}
|
|
|
class Informatica extends Aula {
int impressores;
public Informatica() {
}
public Informatica(String codi, int metres,
boolean llum,
int
consum, int places) {
super(codi,metres,llum,consum,places);
}
}
|
|
|
class Gimnas extends Aula{
boolean dutxes;
int grades;
public Gimnas() {
}
public Gimnas(String codi, int metres, boolean
llum,
int consum, int places) {
super(codi,metres,llum,consum,places);
}
public double espaiEstudiant() {
if (places>0)
{ return (metresquadrats+grades) / places; }
else { return 0; }
}
}
|
|
|
|
|
La classe Espai
disposa de quatre variables (codi de l'espai,
superfície, disponibilitat de llum natural i consum elèctric
per hora). Aquestes variables estaran disponibles
per a qualsevol de les seves subclasses,
cadascuna de les quals també disposarà del mètode public
double consumM2(), un senzill mecanisme de càlcul que ens
permet saber quanta electricitat es consumeix en aquest espai per m2.
A més, la classe disposa de dos
constructors, un que inicialitza
les quatre variables i un altre que no n'inicialitza
cap.
D'aquesta superclasse hereten les classes
Aula i Despatx. Per
a les aules hem previst una utilitat específica, que no necessitarem
per les oficines o departaments: la disponibilitat d'espai per a cada
plaça d'estudiant (mètode public
double espaiEstudiant()). Pels espais "Despatx"
no hem previst cap càlcul específic, només el còmput
de taules i màquines de cafè.
Finalment, definim dues classes que
hereten de la classe Aula,
les classes Laboratori
i Gimnas. "Laboratori"
ens servirà per especificar les particularitats d'aquest tipus
d'espai: en el nostre cas, un comptador de bunsens. En el cas de Gimnas,
tot i ser una Aula, descobrim que el mètode
de la superclasse espaiEstudiant() no
ens serveix, perquè la superfície de la instal·lació
està dividida entre la pista i les grades. Per això n'hem
creat un mètode de càlcul específic:
hem sobreescrit (override)
un mètode de la superclasse.
Ara escriurem un programa per calcular la distribució del consum
elèctric al nostre centre i la disponibilitat d'espai per estudiant
a cada aula:
|
|
|
 |
/**
* Càlcul de consum elèctric i espai de les aules de
l'institut
*
*/
class CalculAules {
public static void main(String args[]) {
//
Creem un despatx
Despatx d1 = new
Despatx("Oficina 1",40,false,600);
d1.maquinadecafe=true;
//
creem un laboratori
Laboratori a1 =
new Laboratori("Laboratori 1",
75,true,1200,20);
a1.bunsens = 12;
//
creem una aula d'informàtica
Informatica a2 =
new Informatica("Informàtica 1",
75,true,6000,15);
a2.impressores =
3;
//
creem un gimnàs
Gimnas a3 = new
Gimnas("Gimnàs",500,true,3000,200);
a3.grades = 2000;
//
Consum d'electricitat
Espai[] espais =
{d1,a1,a2,a3};
System.out.println("Consum
elèctric");
System.out.println("---------------------------------");
double consumtotal=0;
for
(int n=0; n<espais.length; n++) {
consumtotal+=espais[n].consumelectric;
System.out.println("L'espai
"+
espais[n].codi+" consumeix: "+
espais[n].consumM2()+
" W/m2");
}
System.out.println("El
consum total de l'institut és de: "+
consumtotal+
" W\n\n");
//
Càlcul de superfícies per estudiant
Aula[] aules = {a1,a2,a3};
System.out.println("Superfície
per estudiant");
System.out.println("---------------------------------");
for
(int n=0; n<aules.length; n++) {
System.out.println("L'Aula
"+
aules[n].codi+
"
disposa de: "+
aules[n].espaiEstudiant()+
"
m2 per estudiant");
}
}
}
|
|
|
|
|
El càlcul de l'electricitat de les aules forma part de la superclasse
"Espai". Podem cridar el mètode
public double consumM2() des de qualsevol subclasse.
Les aules disposen d'un mètode
específic public double espaiEstudiant(),
que podem cridar des de qualsevol objecte
d'aquesta classe. Els objectes "Gimnas"
tenen lleugerament canviat el sistema de càlcul dels espais.
Segons el llenguatge d'objectes en el
programa hem utilitzat:
- Sobrecàrrega de
mètodes constructors i d'instància.
Totes les classes
disposen de dos constructors,
un amb paràmetres
i l'altre sense. Podem utilitzar els dos indistintament. El compilador
s'encarregarà de decidir quin dels dos ha d'utilitzar quan creem
un objecte de la classe.
Buscarà aquell mètode que s'adeqüi exactament a l'estructura
de paràmetres
que passem. Podem sobrecarregar els
mètodes tantes vegades com vulguem,
sempre que la distribució de
paràmetres sigui única i, si
es donen repeticions, el compilador ens donarà error. Una sobrecàrrega
com aquesta no seria correcta:
class Aula
extends Espai {
public Aula(int metres) {
...
}
//Repetim
constructor amb un únic paràmetre enter,
//No es pot fer!
public Aula(int places) {
...
}
} |
Hem utilitzat sobrecàrrega
de mètodes d'instància a la
classe Aula,
on permetem dues formes diferents de càlcul de la superfície
disponible per estudiant.
- Sobreescriptura de mètodes:
moltes vegades és interessant conservar el nom
i tipus de retorn
dels mètodes
a les subclasses,
però definint-ne un funcionament intern diferent. Nosaltres hem
aplicat aquest criteri a la classe Gimnas.
Els objectes
Gimnas disposen
d'un mètode
public double espaiEstudiant()
aparentment igual a la resta d'aules. El mecanisme de càlcul
del mètode,
però, no és el mateix. Com hem vist, la diferència
queda encapsulada
i es fa invisible per als programes que utilitzin les aules.
- Hem cridat al constructor
de la superclasse amb l'expressió
super(). Si la superclasse
disposa de més d'un constructor,
com podem decidir quin d'ells cridarem a l'hora l'instanciar
les subclasses? super() ens permet
accedir als constructors de la superclasse
seguint un criteri similar a la sobrecàrrega
de mètodes: en funció dels paràmetres
que definim a super() s'activarà un
o altre constructor.
|
|
|
|
|
 |
|
|
|
|