Resum

Cal tenir en compte un conjunt d’aspectes molt importants a l’hora de generar, ja des de l’etapa de disseny, aplicacions que el dia de demà es puguin ampliar i modificar fàcilment. D’una banda, un aspecte aplicable a qualsevol metodologia és el principi d’ocultació. Aquest es basa en el fet que qualsevol classe ha d’actuar com una caixa negra. D’altra banda, existeixen dues eines específiques de l’orientació a objectes, com són l’herència i el polimorfisme. Ambdues permeten tant el disseny com la implementació d’aplicacions altament escalables, mitjançant la possibilitat d’especificar classes que pertanyen a més d’un tipus alhora i d’assignar mètodes diferents a una mateixa operació.

Mitjançant l’herència és possible especificar noves classes únicament indicant les diferències respecte d’altres ja existents. Així es generen jerarquies de classes, amb la reutilització consegüent de codi en darrera instància. Addicionalment, ens permet generar objectes que pertanyen a més d’una classe alhora. El polimorfisme permet associar diferents implementacions, els mètodes, a una mateixa operació dins una jerarquia d’herència. D’aquesta manera, és possible executar codi de manera transparent sense saber a quina classe realment pertany un objecte.

A l’usar el mecanisme d’herència, les classes obtingudes com a especialitzacions d’altres classes s’anomenen subclasses o classes derivades, o classes filles, o classes heretades. Les classes a partir de les quals es dissenyen classes especialitzades, s’anomenen classes bases o classes pare, o superclasses. Cal no confondre els conceptes de ser un/una (corresponent a una relació del tipus especialització o generalització), amb ser una característica/part de (corresponent a una relació d’agregació o composició). Al mateix temps, una classe derivada pot ser una classe base per a altres classes, i donar lloc a una jerarquia de classes.

Una classe derivada hereta tots els membres (dades i mètodes) de la classe base, excepte els constructors. També pot afegir als membres heretats els propis membres (dades i mètodes) i pot sobreescriure els mètodes heretats. Específicament, en el llenguatge Java es pot utilitzar la clàusula extends en la capçalera de la definició d’una classe per indicar que la classe que es defineix és derivada d’una altra classe.

public class NomClasse extends NomSuperClasse {
...
}

Altres aspectes importants són els següents:

  • La classe java.lang.Object és, automàticament, superclasse per a totes les altres classes.
  • Els llenguatges OO acostumen a proporcionar mecanismes per prohibir la possibilitat de dissenyar classes derivades d’una classe determinada. En el llenguatge Java s’aconsegueix utilitzant la clàusula final en la definició de la classe.
  • Cal distingir entre herència simple (classe derivada d’una única classe base) i herència múltiple (classe derivada de diverses classes base). El llenguatge Java no permet herència múltiple.
  • Les variables de referència són polimòrfiques, és a dir, les variables declarades per fer referència a objectes d’una classe X determinada poden ser utilitzades per fer referència a objectes de qualsevol classe Y situada, en la jerarquia de classes, per sota de la classe X (qualsevol subclasse, directa o indirecta, de la classe X). El llenguatge Java proporciona l’operador instanceof per permetre deduir si l’objecte apuntat per una variable pertany a una classe determinada, seguint la sintaxi <nomVariable> instanceof <NomClasse>.

Els mètodes, en el seu interior, poden utilitzar la paraula reservada super amb dues finalitats:

  • En els mètodes no constructors, per fer referència a l’objecte de la classe base i, per tant, poder accedir a les dades heretades en cas que la classe derivada incorpori dades amb el mateix nom que dades de la classe base.
  • En els mètodes constructors, com a nom de mètode per cridar un constructor de la classe base. Ha de ser la primera instrucció del mètode.

Pel que fa al concepte de polimorfisme, aquest es pot aplicar a diversos àmbits, ja que aquest concepte indica múltiples formes i hi ha diversos conceptes en què és aplicable. En efecte:

  • Les variables de referència són polimòrfiques en el sentit que una variable declarada per fer referència a objectes d’una classe X determinada es pot utilitzar per fer referència a objectes de qualsevol classe situada, en la jerarquia de classes, per sota de la classe X.
  • Dins una mateixa classe, derivada d’una altra, hi pot haver diverses dades amb el mateix nom, fruit de declarar una dada amb el mateix nom que el d’una dada heretada. Aquest polimorfisme es coneix com a sobreescriptura de dades. S’aconsella no provocar aquest tipus de sobreescriptura perquè dóna lloc a confusió.
  • Dins una mateixa classe, un mètode pot tenir diverses formes –versions–, i es distingeixen les unes de les altres per la llista d’arguments del mètode. Aquest polimorfisme es coneix com a sobrecàrrega de mètodes (overloading en anglès).
  • Dins una classe derivada es pot canviar la implementació dels mètodes heretats per adequar-ne el comportament a les necessitats de la classe derivada. Aquest polimorfisme es coneix com a sobreescriptura de mètodes (overriding en anglès).
  • Els mètodes són polimòrfics en el sentit que un mètode cridat sobre un objecte executa la implementació del mètode dins la classe a què pertany l’objecte i mai no fa cas de la classe a què pertany la variable de referència emprada per cridar el mètode.

En el context actual de la programació orientada a objectes, s’entén per polimorfisme la propietat que tenen els mètodes heretats d’adequar el comportament a la classe a què pertany l’objecte, la qual cosa és possible gràcies a la sobreescriptura de mètodes i al fet que els mètodes són polimòrfics.

Respecte a la sobreescriptura de mètodes hem de comentar que, com que en Java totes les classes deriven de la classe Object, les classes hereten tots els mètodes que hi ha en aquesta classe. En especial, el Java recomana sobreescriure els mètodes següents:

  • equals(), que permet comparar, per contingut, els objectes de les classes, a diferència de l’operador ==, que compara direccions de memòria, és a dir, diu si les referències apunten al mateix objecte. En el cas de sobreescriure aquest mètode, la documentació oficial de Java recomana sobreescriure també el mètode hashCode() per assegurar que dos objectes que han resultat iguals amb equals(), donaran el mateix resultat amb hashCode().
  • toString(), que proporciona, per a cada objecte d’una classe, una representació en cadena. Aquesta representació és utilitzada per la màquina virtual Java quan necessita una representació en cadena d’un objecte de la classe (per exemple, quan apareix l’objecte dins una concatenació de cadenes).

Vinculat al concepte d’herència i polimorfisme, però vinculat només dins del llenguatge Java, es troba el concepte d’interfície (interface). Una interfície és una maqueta contenidora d’una llista de mètodes abstractes i dades membre (de tipus primitius o de classes). Les seves dades membre, si n’hi ha, són implícitament considerades static i final, i els mètodes, si n’hi ha, són implícitament considerats public.

Una interfície pot ser implementada per múltiples classes, de manera similar a com una classe pot ser superclasse de múltiples classes. Les classes que implementen una interfície estan obligades a sobreescriure tots els mètodes definits en la interfície. Si la definició d’algun dels mètodes a sobreescriure coincideix amb la definició d’algun mètode heretat, aquest desapareix de la classe. Una classe pot implementar múltiples interfícies, a diferència de la derivació, que només es permet d’una única classe base.

Les classes que implementen interfícies han de declarar aquest fet en la clàusula implements de la capçalera de la declaració de la classe.

public class NomClasse implements NomIntefície {
...
}

Una interfície introdueix un nou tipus de dada, de la qual mai no hi haurà objectes propis però sí objectes usuaris –objectes de les classes que implementen la interfície–. Totes les classes que implementen una interfície són compatibles amb el tipus introduït per la interfície. Una interfície no proporciona cap funcionalitat a un objecte (ja que la classe que implementa la interfície és la que ha de definir la funcionalitat de tots els mètodes), però en canvi proporciona la possibilitat que formi part de la funcionalitat d’altres objectes (passant-la per paràmetre en mètodes d’altres classes). La seva presència possibilita la presència d’una jerarquia de tipus (no confongueu amb jerarquia de classes) que permet l’herència múltiple.

Una interfície no es pot instanciar però sí s’hi pot fer referència. Poden derivar d’altres interfícies i, a diferència de la derivació de classes, poden derivar de més d’una interfície. La sintaxi per declarar una interfície és la següent:

[public] interface <NomInterfície> [extends <NomInterfície1>, <NomInterfície2>...] {    
... 
}
Anar a la pàgina següent:
Mapa conceptual