You are on page 1of 118
Capitulo 2 Encapsulacién En este capitulo se describe cémo definir una clase en Java, c6mo de- clarar sus atributos y métodos y cOmo utilizarlos a través de sus objetos. ‘Ademés de explicar c6mo se crean y gestionan en memoria estas instan- cias, se detallan las restricciones de visibilidad de los componentes de Ia clase. 2.1. Definicién de una clase: los atributos ‘A lahora de definir una clase Java debemos especificar la palabra clave ass y asignarle un nombre autoexplicativo. A continuacién, entre los simbolos { y }, se incluirén el conjunto de atributos que caracterizan a todos sus objetos (tipo y nombre), asf como la relacién de métodos que podrén invocar los objetos de otras clases bajo las restricciones derivadas de un conjunto de modificadores. ficador class nombreClase { \ Lista de atributos de la clase \ Lista de métodos de la clase ‘Tales modificadores pueden hacer referencia a la visibilidad de la clase ‘0 su tipo, dos conceptos que explicaremos en detalle a la hora de describir sus aributos y métodos miembro. En concreto, para declarar un atributo de Ja clase es preciso especificar su tipo de datos y nombre asignado, precedido de la lista de modificadores que lo caracterizan. i Escaneado con CamScanner 12 modificadores tipoDatos nombreAtributo; I Los modificadores de tipo permiten especificar que un atributo es esté- tico (stat ic) 0 que su valor es constante (final), mientras que los modifi- cadores de visibilidad (también conocidos como modificadores de acceso) delimitan las clases que pueden acceder al atributo en cuestién (public, private, protected). « Modificador stat ic: Los atributos estdticos, también conocidos co- mo variables de clase, son aquellos cuyo valor es comin para to- dos los objetos de dicha clase. Permiten por tanto que si un objeto cambia el valor de dicho atributo, el resto de objetos de la clase se : vean afectados por ese cambio. Para conseguir este comportamiento | os atributos estdticos se almacenan en una zona de memoria comtn, | compartida por todos los objetos de la clase (en contraposicién a los atributos de instancia, no estaticos, que se almacenan en una posicién de memoria diferente para cada objeto). Como ejemplo de atributo estético podriamos mencionar un contador que registre el ntimero de instancias existentes de una clase dada, y como muestra de atributo de instancia el radio de los objetos de una clase Circulo que modele este tipo de figura geométrica. = Modificador final: Los atributos cuyo valor permanece invariable a Jo largo de toda la ejecucién del programa se marcan con los modifi- cadores final y static, debiendo ser necesariamente inicializados cuando son declarados. « Modificador public: Este modificador indica que el atributo puede er accedido desde cualquier clase del sistema. » Modificador private: En virtud del principio de ocultacién de la POO, los atributos privados s6lo pueden ser accedidos desde la clase a Ja que pertenecen. Tal como describiremos en Sec. 2.2.4, los ob- jetos del resto de clases deberdn invocar ciertos métodos para poder consultar o modificar sus valores. = Modificador protected: Este tipo de modificador de acceso est re- lacionado con la herencia y permite que los atributos protegidos s6lo puedan ser accedidos desde clases que heredan de la propia, o inclu- so desde clases que pertenecen al mismo paquete que la clase donde Escaneado con CamScanner 2.2 Definicién de una clase: los métodos 13 esté definido el atributo. En Java, un paquete no es més que una sen- cilla herramienta organizativa que permite agrupar clases que estén estrechamente ligadas en cl contexto del sistema a desarrollar. Sintéc- tivamente, la definicién de un paquete en Java se articula mediante la palabra reservada package seguida del nombre clegido para dicho paquete. Modificador de acceso “empaquetado”: Este modificador, que carece en Java de una palabra clave especffica, permite establecer que el atributo de una clase sélo pueda ser accedido desde otras clases que se alojan exactamente en el mismo paquete. Empecemos presentando una versién muy preliminar de la clase Circu- lo que iremos extendiendo a medida que ilustremos: més conceptos de la POO. 1 public class Circulo{ private double radio = 1.5; 3) 2.2. Definicién de una clase: los métodos En Java los métodos de una clase se declaran especificando un conjunto de modificadores, el tipo de datos del valor de retorno, el nombre del mé- todo y una lista de pardmetros de entrada entre paréntesis, incluyendo tipo y nombre para cada uno de ellos. Al conjunto de valor de retorno, nombre y lista de pardmetros se le denomina cominmente signatura del método. El cédigo del cuerpo del método se aloja entre los sfmbolos { y }. 1 modificadores tipoRetorno nombreMetodo ( | listaDeParamet ros) { ' yy ‘Veamos cémo se articula dicha sintaxis en el caso concreto de nuestra clase Circulo: Escaneado con CamScanner 14 Encapsulacién | public class Circulo( private double radio ~ 1.5; public double getArea (){ s return 3.1416 * radio * radio; } public double getPerimetro(){ ° return 2 * 3.1416 * radio; } ut} Existe un método en Java que merece una menci6n especial por ser el que marea el comienzo de ejecuci6n del programa, Tal como muestra el c6digo indicado a continuacién, dicho método, denominado main, permite acceder a cada uno de los argumentos de entrada del programa a través de las sucesivas posiciones del array de cadenas args. 1 public static void main(String[] args) ( // Argumentos de entrada accesibles mediante args [0], args[1]... | a} 2.2.1. Métodos constructores Los constructores son los métodos que permiten instanciar las clases, esto es, crear objetos de las mismas. Algunas de sus propiedades més rele- vantes son descritas a continuacién: » Tienen necesariamente cl mismo nombre que la clase, = Carecen de valor de retorno (ni siquiera se especifica la palabra clave void). « Se invocan con el operador new para crear un objeto de la clase. Una vez creado dicho objeto puede asignarse a una referencia que utiliza- rin otras clases (a través del operador .) para acceder sus atributos € invocar sus métodos, siempre que sus respectivos modificadores de visibilidad lo permitan. Escaneado con CamScanner 2.2 Definici6n de una clase: los métodos 15 1 public class Test { Circulo ref = new Circulo (3.4); : ref.getArea(); //getArea es ptblico //ref.radio no seria posible porque radio es un atributo privado de la clase Circulo » Existen varios tipos de métodos constructores: El constructor por defecto (no-args constructor), que se declara implicitamente en cualquier clase Java en la que el programador no haya declarado ningtn constructor. © El constructor que recibe como pardmetros de entrada valores para poder inicializar los diferentes atributos de la clase. « El constructor de copia que crea un objeto copiando los valores de los atributos de uno dado como parfmetro de entrada, A continuacién, completamos Ia clase Circulo incorporando los tres tipos de constructores indicados arriba. relass Circulo private double radio = 1.5; Cirevlo() no-args constructor ) double r){ c){ //constructor de copia 's public double cetArea(){ return : + radio * 3.1416; 8 public double return 2° etro(){ * radio; Lo Escaneado con CamScanner 16 Encapsulacién En el extracto de cédigo mostrado a continuacién se incluye la invoca- cién a cada tipo de constructor, resultando tres objetos de la clase Circulo representados en notacién UML en Fig. 2.1. Circulo cl = new Circulo(); 2 Circulo c2 = mew Circulo (5.2); Circulo c3 = new Circulo (c2); radio: double + Cireulot) accu ciclo siirulo tadio= 15 20°52 ratio= 5.2 Figura 2.1: Instanciacién de Ia clase Circulo en notacién UML. 2.2.2, Clases y objetos. Gestién de memoria En la seccién 1.5 ya adelantamos que una de las tareas fundamenta- les de la JVM es la gesti6n eficiente de la memoria. En concreto, la JVM distribuye la memoria en dos zonas denominadas heap y stack (pila) que albergan datos de diferente naturaleza. El heap, gestionado por una entidad denominada recolector de basura (garbage collector), es el espacio de me- moria en tiempo de ejecucién donde se registran los objetos, mientras que en la pila se almacenan las variables locales, las Hamadas a los métodos (incluyendo tanto los valores de los pardmetros de entrada como el resul- tado), las variables primitivas y las referencias a los objetos del heap. Por ejemplo, como resultado de Ia instanciacién de la clase Circulo mostrada en Fig. 2.1, las referencias c1, c2 y c3 registradas en la pila apuntarén a los tres objetos Circulo del heap (ver Fig. 2.2). inicialmente asignadas haciendo Si ahora modificamos las referencias Escaneado con CamScanner 2.2 Definicién de una clase: los métodos 7 PILA HEAP a. | _ Girewlo Ieeceneejner eal radio = 1.5 2 —$ | cirevlo radio = 5.2 3 —4|_ ciraulo radio = 5.2 Figura 2.2: Estado de la pila y el heap tras la instanciacién de la clase Circulo. c2 = cl, el resultado seria que el circulo de radio 1.5 pasarfa a estar apun- tado por dos referencias desde Ja pila, tal como se muestra en Fig. 2.3. Los objetos registrados en el heap permanecen en memoria mientras existas una referencia valida que los apunte desde la pila. En el momento que dicha referencia desaparece, el recolector libera la memoria ocupada por el objeto. Este serfa precisamente el caso del cfrculo que estaba apun- tado por c2 en Fig, 2.2, cuya referencia deja de existir tras la asignaci6n c2 = cl, tal como se representa en Fig. 2.3. Ademis, es posible crear objetos sin asignarles ninguna referencia (0 nombre) desde la pila, los cuales se usan en el contexto en el que son ne- cesarios y después de desechan. Tales objetos, denominados por razones obvias andnimos, ayudan a optimizar la gesti6n de memoria evitando que sobrevivan en el heap instancias que ya no scrdn utilizadas posteriormente enel sistema (y que el recolector no liberar4 automAticamente por tener un referencia valida -aunque no utilizada- desde Ia pila). En relacién a los métodos, nétese que éstos se guardan en la pila por estricto orden de invocacién y desaparecen tinicamente de 1a misma cuando se han ejecutado correctamente y han entregado su valor de retorno. Esta informacién serd de suma utilidad para los mecanismos de gestién de ex- Escaneado con CamScanner 18 Encapsulacton PILA HEAP a Cireulo radio #15 a Cireulo radio = 5.2 3 ———————— _ Cirevlo radio = 5.2 Figura 2.3: Modificacién de las referencias a los objetos de la clase Circulo. cepciones de Java de los que nos ocuparemos en el capitulo 5. 2.2.3. Definicién de un método. Restricciones de tipo y visibili- dad Los modificadores de tipo y visibilidad introducidos en Sec. 2.1 en re laci6n a los atributos de la clase también se aplican a sus métodos. En lo tocante al tipo podemos definir tanto métodos estaticos (static) como abs- tractos (abstract) y finales (final). = Modificador static: El comportamiento de los métodos estaticos © métodos de clase no depende del objeto concreto sobre el que son invocados, de ahi que, para simplificar la gestién del heap, se recomiende que sean Hamados directamente sobre el nombre de la clase en lugar de usar una instancia de la misma (e.g., haciendo Circulo.getContadorInstancias ())). Por el contrario, los méto- dos de instancia devuelven valores diferentes segtin el objeto usado en la llamada, raz6n por la que deben ser necesariamente invocados sobre una instancia de la clase (e.g. cl.getArea () ). Escaneado con CamScanner 2.2 Detinieiin de una clase: los métodos: 9 Moditicador abstract: Los métodos abstractos son aquellos que no estin implementados porque, si bien modelan un comportamiento propio de la clase a la que pertenccen, su implementacién requie- wv informacisn no disponible cn la misma, Por ejemplo, el método getArea() es abstracto en In clase FigGeo (referida a las figuras 8) porque el valor del fren depende de pardmetros especf- tipo conereto de figura (c.g., el radio en un cfrculo, Ja tura en un tridngulo, etc.). base y Moditicador final: La implementacién de los métodos finales no puede ser modificada, raz6n por la que las clases hijas de aquéllas en la que dicho método esté definido no pueden versionarlo o adaptarlo (en realidad, en nomenclatura Java deberfamos decir que los méto- dos finales no podrén sobreescribirse, tal como detallaremos en el pitulo 3). Modificador public: Este modificador indica que el método puede ser invocado desde cualquier clase del sistema. Moditicador private: Este tipo de métodos s6lo pueden ser invoca- dos desde la clase en la que estén definidos. Se utilizan para optimizar 1a estructuracién del cédigo de la clase. Modificador protected: Los métodos protegidos de una clase sélo puedan ser invocados desde clases que heredan de ésta 0 que compar- tan el mismo paquete que ella, Modificador de acceso “empaquetado”: Este modificador permite es- tablecer que el método de una clase sélo pueda ser llamado desde otras clases que se alojan exactamente en el mismo paquete. En numerosas ocasiones los métodos de un objeto necesitan acceder a los atributos de! mismo. Para poder referirse al objeto al que pertenecen dichas atributos y m Cirevlo (double r){ radio = ry n Cireulo (Circulo c){ radio radio; double getRadio (){ return radio; w void setRadio (double radio) { this. radio = radio; a) n public double getArea(){ return radio * radio * 3.1416; a} 2 public double getPerimetro(){ return 2 * 3.1416 * radio; |» } 2.2.5. Paso de pardémetros Los paradigmas de programacién establecen dos tipos de paso de pard- metros en Ia cjecucién de las funciones: « Paso de pardmetros por valor, En este caso la funcién recibe una co- pia del valor del parémetro especificado, raz6n por la que los cambios realize bre dicha copia no son visibles desde fuera de la fun- cién. £44 +s debido a que el pardmetro pasado y la variable que usa laf su: etlerpo se corresponden con dos posiciones de me- moria dilirontes en las que se almacena exactamente el mismo valor Escaneado con CamScanner 22 Encapsulaci6n, cuando se invoca dicha funcién. Por ende, las modificaciones que ha- ga la funcién sobre su celda de memoria no afectan al contenido de} pardmetro pasado a la misma, alojado en otra posicién diferente (y por consiguiente protegido de tales cambios). = Paso de pardmetros por referencia. En este caso la funci6n recibe la direccion de memoria donde se almacena el parémetro pasado en la que se registra un valor inicial. Si la funcién realiza operaciones sobre dicha posicién de memoria, afectando asf a su contenido original, esos cambios sf serdn visibles al término de su ejecucién. Dicho de otra forma, si una funcién Gnicamente necesita consultar los valores de los parémetros de entrada (para operar con ellos y volcar el re- sultado en otras variables), el paso debe ser por valor. Si por el contrario queremos que la funcién devuelva un resultado a través de dichos paréme- tros, éstos deberdn ser pasados necesariamente por referencia. En Java el paso de pardmetros cuando queremos invocar el método de una clase es siempre por valor. En el caso de los objetos lo que se pasa por valor al método es la referencia al objeto y no el objeto en si mismo, de forma que es posible que un método realice cambios sobre un objeto recibido como pardmetro y que dichas modificaciones sean visibles tras su invocacién (debido a la existencia de dos referencias diferentes en la pila que apuntan a un mismo objeto -el que se ha modificado- en el heap). 2.3. Cuestiones de revisién Cuestién 1 Indique si el cédigo Java mostrado a continuacién es correcto. En ca- so de detectar algtin error, expliquelo y describa una posible soluci6n en sintaxis Java. public class Prueba{ private int entero; private String cadena; public Prueba (int i, String cadena) { entero = ij; cadena = cadena; Escaneado con CamScanner 2.3 Cuestiones de revision public int getEntero (){ return entero} public static void main (String[] args)! system.out.printin ("El entero es: " + Prueba. getEntero())F Solucién El cédigo indicado presenta dos errores: = En primer lugar, el uso de la referencia this es obligatorio en la linea 7 para poder deshacer la ambigtiedad derivada del nombre que comparten tanto el atributo de la clase Prueba como el segundo de los pardmetros de su método constructor (cadena). «= En segundo lugar, la invocacién del método getEntero en Ia linea 15 también es errénea, dado que al ser éste un método de instancia (no estdtico) es preciso crear un objeto de Prueba (anénimo en la solucién indicada a continuacién) para poder realizar dicha Namada. ) public class Prueba{ private int entero; 3 private String cadena; 5 public Prueba (Ant i, String cadena) { entero = if 7 this.cadena = cadena; } public int getEntero (){ " return entero; } public static void main (String[) args){ Is System.out.println ("El entero es: " + new Prueba 5, "mi cadena").getEntero())i Escaneado con CamScanner 24 Encapsulacién Cuestién 2 Considere las clases Clase1 y Clase2 mostradas a continuacién. 1 public class Clasel{ public int atributo; 3 public Clasel (int a){ atributo = a; so} } t 7 public class Clase2{ 9 public double atributo; public Clase2 (double a) { H " atributo = a; ) a} Explique cudl es el resultado de ejecutar el siguiente cédigo Java. 1 public class Test { public static void main (String[] args){ , int entero = 25; Clasel objl = new Clasel (1); s Clase2 obj2 = new Clase2 (2.2); metodo (entero, objl,obj2); 7 System.out.printin("El valor del entero es: * + entero); System.out.print1n("El atributo de objl es: * + objl.atributo); 9 system.out.print1n("El atributo de obj2 es: * + obj2.atributo); } public static void metodo (int e, Clasel cl, Clase? c2){ e = 100; n cl.atributo = 5; c2 = new Clase2 (4.7); Escaneado con CamScanner 2.3 Cuestiones de revisi6n 5 1s system. out .printIn(*E] atributo de c2 es * + 2, | atributo); | } Solucién La salida por pantallla presenta los siguientes mensajes: El atributo de c2 es 4.7 El valor del entero es 25 El atributo de objl es 5 El atributo de obj2 es 2.2 Nétese que método recibe por valor tanto la variable entera (de ahf su valor 25 al retornar de la ejecucién) como las referencias obj1 y obj2 (a los objetos de Clase1 y Clase2, respectivamente). La referencia al primero no sufre modificaci6n alguna, raz6n por la que es posible ver desde obj1 el nuevo valor que el método establece para su atributo (5). Por el contrario, lareferencia al objeto de Clase2 sf se modifica desde el método (linea 14), de ahf que el valor establecido en su cuerpo (4.7) no afecte a Ja instancia apuntada por ob32 (que conserva su valor inicial 2.2 establecido en Ia linea 5). Cuestién 3 Indique cudl es el resultado de ejecutar el siguiente cdigo Java: 1 public class Test { int i; 3 String 5; double d; public Test (int 4, String 8, double d){ > this. t=4; this.s=s; + this.ded; ) public Test (){ ® this(1,"mi cadena", 2.5); Escaneado con CamScanner ” n s n w » ” 6 Encapsulactiy public Test clone () | return new Test (1,9,d)i 1 public static void metodol (Test obj) ( obj. 4-200 ) public static void metodo2(Test obj) { obj new Test ()j obj.i +=400; system.out.printin("El valor del entero dentro de metodo2() es " + obj.i)i ) public static void main (String{] args){ Test obj = new Test (); Test obj2 = obj.clone(); system.out .println("El atributo entero de obj2 es "+ obj2.i); metodol (obj); system.out.println("El valor del entero tras metodol() es * + obj.i)j metodo2 (obj) i System.out.println("El valor del entero tras metodo2() es " + obj.i); Solucién La salida por [ £1 atributo entero de obj2 es 1 | fl valor del entero tras metodol() es 200 fi valor del entero dentro de metodo2() es 401 £1 valor del entero tras metodo2() es 200 El atributo i de ol 2 registra un valor | debido a que esta instancia resulta de haber clonado el objeto obj. Por su parte, metodol () actualiza (4.200) el valor de! atributo entero del objeto apuntado por la referencia ob} Escaneado con CamScanner 2.3 Cuestiones de revisién 27 (Ifnea 21). Dado que el método no modifica dicha referencia este cambio es visible en cl método main tras retornar de met odo (). Por el contrario, metodo2() cambia la referencia inicial obj pasando a apuntar a una nueva instancia de la clase Test (Ifnea 25), de ahf que los cambios efectuados en su atributo entero (de valor 401) no afecten en absoluto a Ia instancia referenciada por obj (donde prevalece el valor 200 establecido durante la ejecucién de metodol (). Cuestién 4 Considere las clases Java mostradas a continuaci6n e indique si las si- guientes afirmaciones son verdaderas 0 falsas. public class Persona{ 2 private String nombre; private int edad; public Persona (String n, int e){ 6 nombre i edad = e; 8} w public setEdad (int e){ edad = e; nw} 4 public static ArrayList actualiza (Persona p, int edad) { ArrayList lista = new ArrayList (); 6 p.edad = edad; lista.add(p); 6 lista.add(new Persona ("Ana Laura",29).setEdad (30) Vi return lis n public class «~~ public sta’ Persona 30); © void main (String[] args) { iza (mew Persona ("Ana Laura",29) _| Escaneado con CamScanner 28 : -_ Encapsulacién 1, La invocacién del método actualiza desde la clase Test ¢s ineo rrecta. Tras la ejecucién del método actualiza en el heap habré un Gni- co objeto de la clase Persona referido a una mujer de nombre Ana Nv Laura y 30 aitos de edad. Neale tic is 3. Eluso de objetos anénimos en la implementacién proporcionada para el método actualiza hace que cl uso de memoria sea ineficiente. 4. El cédigo podria implementar exactamente la misma funcionalidad sin necesidad de incluir el método set Edad en la clase Persona. Solucién 1. Falso. La llamada es correcta toda vez que el método es estdtico y por consiguiente puede invocarse utilizando el nombre de la clase en Ja que esté definido (en lugar de usar un objeto de la misma). 2. Falso. El heap registra dos objetos referidos a Personas de idénticos nombre y edad (creados en las Ifneas 18 y 25). 3. Falso. Es més eficiente usar objetos anénimos ya que su memoria se libera inmediatamente en cuanto se ejecuta la instruccién para la que han sido creados, 4. Verdadero. En la linea 18 podrfamos crear el objeto anénimo con una edad de 30 aiios (lista.add(new Persona(“Ana Laura”,30))) zar ese atributo a 29 y actualizarlo mediante el en Jugar de inicis metodo setter, Escaneado con CamScanner Capitulo 3 Herencia Dado que uno de los pilares de la POO es la capacidad de reutiliza- cién de cédigo, en este capitulo nos centramos en los mecanismos de he- rencia que habilitan este tipo de propiedad tanto a través de relaciones de generalizacién-especializacién como mediante la implementacién de inter- faces. 3.1. Relaciones de generalizacién-especializacién Laherencia permite identificar relaciones de generalizacién-especializa- cién entre dos clases, en la que la mas general adopta el rol de clase padre (0 superclase) y la més especffica se significa como clase hija (0 subcla- hija hereda todo el comportamiento definido en la clase padre tributos) salvo los constructores, pudiendo adems extenderla afiadiendo toda la informacién que se considere oportuno. En concreto, las clases hijas pueden incorporar atributos espectficos que las caracterizarin ores de los heredados de su clase padre, asf como nuevos métodos am que hab formas de interaccién adicionales con otras clases. Incluso las clases } tienen la opcién de “versionar” o “adaptar” aquellos métodos de la ci dre cuya implementacién no les resulte itil, adecuando asf su funci nto al contexto de cada sistema. Las relaciones de herencia se mode ‘no asociaciones del tipo is-a (“‘es-un”) dado que los objetos de la cl jo son también objetos de la clase padre. Tal como veremos en el capiuio 4, el polimorfismo explotaré los diferentes tipos que la herencia conficre a los objetos para facilitar una programacién genérica y reutiliza- ble. 29 Escaneado con CamScanner Herencia A la hora de definir relaciones de generalizaci6n-especializacién en Ja- ‘va estamos limitados no sélo por la cardinalidad de los mecanimos de he- rencia (i.c., cl mimero de clases padre de las que puede heredar una dada) sino también por el tipo de la clase que pretenda adoptar el rol de supercla- se. «= En lo tocante a la cardinalidad, en Java se contemplan Gnicamente relaciones de herencia simple en las que una clase hija extiende a una inica clase padre (si bien esta limitacién se puede salvar f4cilmente recurriendo a las interfaces, tal como detallaremos a Io largo de este capitulo). En relacién a la tipologfa, Java permite caracterizar una clase como final. De forma similar a los atributos y métodos que también pue- den ser de este tipo (recordar secciones 2.1 y 2.2.3), las clases finales no pueden ser modificadas y, mas concretamente, no pueden ser ex- tendidas por otras clases del sistema. Sirva como ejemplo Ia clase Cuadrado que podria ser definida como final toda vez que no hay diferentes tipos de esta figura que sea interesante modelar como hijas (en contraposici6n a lo que ocurre con Triangulo que podria ser ex- tendida para incorporar tridngulos equildteros, isdsceles o escalenos). A continuacién se muestra un fragmento de cédigo en el que se articula una relacin de herencia entre las clases FigGeo y Circulo, donde pode- mos observar tanto los atributos que los cfrculos heredan por ser figuras geométricas (color y relleno) como el atributo que caracteriza especifi- camente a este tipo de figura (radio). 1 public class FigGeo { private String color; } private boolean relleno; s public String getColor ()( return color; 7 ) » public boolean getRelleno () | return relleno; n 1 public void setColor (String c){ color = cj is) Escaneado con CamScanner 3.1 Relaciones de generalizacién-especializacion 31 7 public void setRelleno (boolean r){ relleno = rj wt ) public class Circulo extends FigGeo{ 2 private double radio; as public double getRadio() { return radio; » x» public void setRadio (double r) { radio = rj ao} ms Circulo (double r){ radio = r; 3x} 3.1.1. Restricciones de visibilidad: modificador protected y re- ferencia super Ya adelantamos en las secciones 2.1 y 2.2.3 que los modificadores de visibilidad de los atributos y métodos de una clase permiten delimitar el acceso a los mismos desde otras clases. Asf, el modificador public no impone restriccién alguna permitiendo que estos componentes puedan ser accedidos desde cualquier otra clase del sistema, mientras que el modifica- dor private impide que tales métodos y atributos puedan ser consultados ylo modificevios desde fuera de la clase en la que estén definidos. Uno de Jos modi crores contemplados en Java (protected) esté estrechamen- te relacionados con los mecanismos de herencia a través de relaciones de generalizacin-espe icién, de modo que los atributos y métodos prote- gidos de una clase s6lo podrdn ser accedidos desde clases que la extiendan (i.e. sus hijas) o bien clases que pertenezcan al mismo paquete que ella. Por su parte, la visibilidad empaquetada (sin ningin tipo de modificador explt- Cito reservado en Java) restringe atin mds el acceso protegido posibilitando que los componentes de la clase s6lo puedan ser consultados y/o modifi- cados desde otras clases ubicadas en su mismo paquete. En Ja Tabla 3.1 Escaneado con CamScanner 2 ; : Herencia recordamos los cuatros tipos de visibilidades definidos en Java, aplicables a los atributos y métodos de cada clase, Diferente Modificador Misma clase Mismo paquete Subclase - paquete public v v v protected v v x v x x private v x x Tabla 3.1: Modificadores de visibilidad en Java. Si bien los mecanismos de herencia permiten que la relacién de atri- butos y métodos de una clase padre sean directamente heredados por sus conviene tener claro que no debemos confundir el disponer de ese comportamiento con poder accederlo sin restricci6n alguna, Dicho de otra forma, las clases hijas heredan los atributos de su padre, pero Gnicamente podrin consultar y modificar sus valores si la visibilidad fijada en la clase padre lo permite. Si un atributo es privado en el padre, la clase hija no podré accederlo directamente sino que deberd solicitar a la superclase que haga la consulta o modificacién en su nombre. En concreto, para poder permitir que una clase hija pueda ser instanciada y que sus atributos (los propios y los heredados del padre) puedan ser inicializados, Java exige que la primera sentencia del constructor de la clase hija sea una invocacién al constructor de la clase padre. Dicha llamada, tfpicamente, incluye una relacién de los valores correspondientes a los atributos no visibles para el hijo a los que el padre pueda acceder sin problema. Dado que los métodos constructores Ge la clase padre es el tinico comportamiento que las clases hijas no here- dan, la invocaci6n se realiza a través de una referencia llamada super (que también se empleard para invocar el resto de los métodos de la superclase, tal como veremos en los ejemplos ilustrados en este capitulo). Enel extracto de cédigo mostrado a continuacién se aprecian las lama- das desde cada uno de los constructores de Ja clase Circulo al constructor de la superclase FigGeo. En el constructor por defecto (Ifnea 17) se usa Ja refencia super sin par4metros de entrada porque no se desean inicia- lizar los valores de los atributos color y relleno (privados en FigGeo) del objeto en cuestién, que es precisamente lo que se hace en el segundo de los constructores mediante la invocacién super (color, relleno) dela Inea 22. Merece Ja pena destacar que, en caso de que el programador no Escaneado con CamScanner 3.1 Relaciones de generalizacién-especializacién 33 llame explicitamente al constructor de la superclase, ser4 Java quien reali- ce tal invocacién. En ese supuesto, la Hamada serf siempre al constructor por defecto (no-args constructor) de Ja clase padre (por tanto mediante una llamada super ()), produciéndose un error de compilacién si éste no esté definido. Dado que Java define un constructor por defecto inicamente en las clases que no disponen de ningun otro, es recomendable o bien alojar siempre un no-args constructor en las clases que puedan ser extendidas en nuestro sistema (por si olvidamos hacer la Hamada desde la clase hija, de- legando esa tarea en Java). o bien Hamar explicitamente a alguno de los constructores del padre (desde la clase hija) mediante la referencia super oportuna. " ew r public cla private private boolean re!) ° public FigGeo |) public F 7 color, boolean relleno) { this.color this. ° Llenos public cla: extends FigGeo{ private o3 ing color, boolean relleno, relleno); radio; - —} Escaneado con CamScanner 34 Herencig ——n—w,n m0 mo>dw0ETE TN 3.1.2. Sobreescritura de los métodos de una clase padre Hemos adelantado al comienzo del capftulo que las clases hijas puedan modificar las implementaciones de aquellos métodos heredados que no les resulten validas. A este proceso se le conoce con el nombre de sobrees. critura (overriding). Para poder motivar su utilidad fijémosnos en Ia clase Object que extienden todas las clases Java, En virtud de la herencia, los métodos implementados en la clase Object de la API de Java pueden ser invocados desde cualquier clase (siempre que sus modificadores de visi dad Jo permitan) 0 incluso sobreescritos (en caso de que la implementacién heredada de Object no sea practica). Dos de los métodos de Object més comtinmente versionados en sus clases hijas son los que permiten repre- sentar un objeto como una cadena (String toString()) y detectar si dos (referencias a) objetos dados son iguales (boolean equals (Object 0). « El método toString() en la clase Object representa cada objeto como una cadena en Ia que se incluye el nombre de su clase, el sim- bolo @ y la direccién que ocupa dicho objeto en el heap en formato hexadecimal. Parece evidente que la representaci6n de un objeto co- mo cadena podria ser més util si incluyera Ja relacién de atributos con sus respectivos valores, raz6n por la que la mayorfa de las clases de un sistema incluyen una versién propia del método toString(). El resultado de este proceso en la clase Circulo aparece mostrado a continuacién, donde en la linea 30 se aprecia una invocacién al método toString() de la clase padre FigGeo (Ifnea 13) para poder acceder a los valores de sus atributos privados (color y relleno). Nétese que, por el contrario, el atributo radio puede consultarse di- reclamente, aun siendo también privado, por ser un atributo propio de la clase Cireulo. 1 lass Geo! ring color: boolean relleno; ie PigGeo(){ public FigGeo (String color, boolean relleno){ lo this.color = color; "La declaracién extends Object esté implicita y por tanto es opcional en la declara- ci6n de cualquier clase Java, Escaneado con CamScanner 3.1 Relaciones de generalizacién-especializacién 35 this.relleno = relleno; " ) public String toString (){ return "Color:" + color + "R 1s ) public class Circulo extends FigGeo/ » private double radio; s) public double getRadio(){ return radio; public void setRadio (double r){ ng toString (){ urn super. toString ()+"Radio:"+radio; » Respecto al método equals, su implementacién por defecto no re- sulta de utilidad en las clases que extienden Object por su genera- lidad. En concreto, dicha implementacién compara dos referencias de la pila y determina que son iguales cuando ambas apuntan a un mismo objeto en el heap. Evidentemente esta implementacién no re- sulta prdctica para comparar, por ejemplo, dos cfrculos, raz6n por la debemos sobreescribir el método equals en la clase correspondiente para asf poder cotejar sus respectivos radios, tal como se muestra en el extracto de cédigo incluido a continuacién, Nétese que el acceso aun atributo especffico de este tipo de figura geométrica (y por ende ausente en la clase Object) obliga a convertir el objeto oa Circulo (linea 22), evitando asf un error que acusarfa el propio compilador. Las pautas que dictan este tipo de conversiones serdn analizadas en el capitulo 4 cuando ahondemos en el concepto de polimorfismo. boolean equals (Object 0) { 2 this == 0; i Escaneado con CamScanner Herenci 4 public class Circulo extends FigGeo! private double radio; public double getRadio(){ ~~ return radio; } public void setRadio (double r){ “4 radio = rj } public String toString (){ 4 return super. toString ()+"Radio } +radios public boolean equals (Object o){ 2 return radio == ((Circulo)o). radio; } uy Restricciones de la sobreescritura de métodos El proceso de la sobreescritura de los métodos heredados de una clase padre esté regido por las restricciones descritas a continuacién: = El método sobreescrito en la clase hija debe respetar la signatura de- finida por el padre (esto es, el tipo de valor de retorno, ¢! nombee del método y la lista de pardmetros de entrada). Sin embargo, la vi- sibilidad del método sobreescrito puede ser idéntica a la definida en Ja superclase o puede ser modificada, siempre que no se debilite. Es decir, una subclase puede abrir Ia visibilidad del método que sobre~ escribe pero nunca hacerla més restrictiva que la especificada en la clase padre, De acuerdo a la Tabla 3.1, si un método es protected ea . podré ser piiblico © protegido en la clase hija, Andlo- gamente, si un método es piiblico en la clase padre también lo seré en la clase hija. No es posible sobreescribir los métodos no accesibles desde la clase hija (.e., privados 0 empaquetados en caso de que la subelase y la Escaneado con CamScanner At Relaciones de generatiznetdneespectallanclin ” superelase pertenezean a diferentes paquetes), Los metodos estiticns (stat Le) pueden ser heredados pero nunca xo: brveseritos en las clases hijas, toda ver que esta préetica conducirfa a implementaciones diferentes en li subelase y en la superclase cuan- do, en virtud de su naturateza estiticn, ef comportamiento deberfa ser (recuérdese que li herencia impone que las instancias conmin aaa de La elise hija también Lo son de la clase padre). © Los metodos finales (final) pueden ser heredados pero nunca sobre- s clases hijas, debido a que la implementacién definida se no puede verse modificada, eseritos en en la supere! Sobre: ture vel sobrecarga Las scritura no es el inico mecanismo que permite la convivencia de un coniunte de métodos con idéntico nombre; existe también la sobre- carga (over /ouding) en la que la diferencia entre métodos homénimos es- triba en la lista de parimetros de entrada y valores devueltos, En oposicién ala sobreescritura —en la que se modifica la implementacién de un método que ya existe en la clase padre, la sobrecarga permite afadir nuevos com- portamientos en una clase. Sin haber hecho alusién al término en sf, a lo largo de este capitulo ya hemos visto algunos ejemplos de métodos sobre- cargados, en concreto, los constructores de las clases Circulo y FigGeo. realidad, la tinica condicién para sobrecargar un método en una clase es que no exista ambigiicdad sintéctica entre ellos, esto es, que no compartan el mismo mimero y tipo de pardmetros. 3.1.3, Clas s y métodos abstractos Las directrices de la herencia nos pueden evar a identificar ciertos comportamientos de una clase que no pueden ser implementados por la misma, fundamentalmente por falta de informacién, Por ejemplo, todas las tienen Grea; sin embargo, su valor no puede calcularse figuras geométri sin conocer el tipo conereto de figura, Para poder modelar este tipo de tuaciones se usan las clases y métodos abstractos que ya mencionamos en el capitulo 2. Las clases abstractas (marcadas mediante el modificador abstract) son aquéllas que no pueden instanciarse, esto es, de las que no podemos Crear objetos mediante la invocacién de alguno de sus constructores a través Escaneado con CamScanner 38 Herencia del operador new. Por su parte, los métodos abstractos carecen de imple- mentacién en una clase, y se identifican a nivel sintéctico por incluir el + en la declaracién (en lugar de las Haves que abrazarfan su cédigo se implementado), ademés del modificador abst ract. En el mo- mento en el que al menos uno de los métodos de una clase es abstracto ésta se convierte automaticamente en abstracta también, raz6n por la que una clase que extiende a otra abstracta (c.g., Circulo que hereda de FigGeo) debe implementar necesariamente todos sus métodos abstractos si no quiere perder la capacidad de ser instanciada, Este comportamiento aparece ilus- trado en el cédigo mostrado a continuaci6n, donde el método get area (). abstracto en la superclase FigGeo (Ifnea 17), se ha implementado en su clase hija Circulo (linea 35). public class abstract FigGeo! private String color; private boolean relleno; Protected FigGeo () { + protected FigGeo (String color, boolean relleno) this.color = color; 0 this.relleno = relleno; public String toString(){ “ return *Color:" + color + "Relleno:*+rellenoy public abstract double getArea()i » public class Circulo extends FigGeo| private double radioj n public Circulo (String c, boolean r, double 133) “ super (c,1)i radio = radj » » public double getRadio () ( return radio; w ) Escaneado con CamScanner 2 w 6 3.2 Implementacién de interfaces 39 public void setRadio (double r) ( radio = rj } public String toString (){ return super.toString()#"Radio:"+radio; } public double getArea () { return radio * radio * 3.1416; } Si bien una clase abstracta puede disponer de una implementacién para todos sus métodos, es comtin que esto no ocurra en un sistema real donde Lipicamente dicha clase adoptaré el rol de padre para un conjunto de clases hijas instanciables (que serén por tanto las que implementen los métodos abstractos heredados). En consecuencia, la utilidad de una clase abstracta no esté ligada a la posibilidad de crear objetos de ella, sino a la capacidad de explotar su tipo como clase padre, comtin por tanto a todas sus subcla- ses; este tipo de practicas serén de gran utilidad a Ja hora de plantear una programaci6n polimérfica y genérica, tal como detallaremos en el capitu- lo 4. A pesar de no poder instanciarse, la forma en la que se articula la he- rencia impone que las clases abstractas dispongan igualmente de métodos constructores, los cuales serén invocados tinicamente por sus subclases (de ahi que su visibilidad sea normalmente protected) para asf poder iniciali- zar atributos inaccesibles para ellas, tal como se aprecia en las Ifneas 8 y 24 de los e6digos de las clases FigGeo y Circulo, respectivamente, mostradas anteriormente. 3.2. Implementacién de interfaces Ademis de las relaciones de generalizacién-especializacién exploradas hasta este punto del capitulo, los mecanismos de herencia habilitan una for- ma alternativa de reutilizacién de cédigo con ventajas muy significativas desde el punto de vista de la programacién genérica. Se trata de la imple- Escaneado con 40 Herencia mentaci6n de interfaces, las cuales permiten relacionar entre sf un conjunto de clases (tipicamente muy dispares) definiendo un mismo tipo para todas ellas que es precisamente el nombre de la interfaz. En realidad, la herencia Permite que los componentes de la interfaz (esto es, la relacién de atributos y/o métodos definidos en ella) pueda ser heredados por todas las clases que Ja implementan. 1, Componentes de una interfaz En Jas interfaces se identifican comportamientos genéricos que son co- inunes a un conjunto de clases, cuya implementacién particular depende precisamente de las mismas, Pensemos en una interfaz que permita re- lacionar entre sf la heterogencidad de clases relativas a objetos de la vi- da real que puedan ser visitados en un horario particular (¢.g., Hospital, 2oologico y MonumentoHistorico). Dicha interfaz, a la que denomina- remos Visitable tal como se aprecia en el extracto de cédigo mostrado a continuacién, podrfa incluir sendos métodos para consultar y modificar el horario de visita (denominados String getHorarioVisita() y void setHorarioVisita (String horario), respectivamente) cuya implemen- tacién dependerd claramente de cada una de las clases anteriores, toda vez que los horarios para acceder a 200s, monumentos hist6ricos y hospitales no tienen que ser necesariamente los mismos. Esta es la raz6n por la que, hasta la versién 8 de Java, en las interfaces slo se podian definir métodos piblicos (para que cualquier clase pueda heredar el comportamiento de la interfaz) y abstractos (porque su implementacién no puede ser concretada en la interfaz en ausencia de la informacién particular de cada clase). De he- cho, ambos modificadores (public y abst ract) estén implicitos, pudiendo por ello omitirse en la declaracién de la interfaz. public interfaces Visitable{ public abstract String getHorarioVisita(); void setHorarioVisita(String horario)i } La herencia articula los mecanismos precisos para que la relacién de métodos abstractos disponibles en la interfaz Visitable sean directamente heredados por todas las clases que la implementen. Segiin lo comentado en seccién 3.1.3, en caso de que éstas deseen ser instanciables (i¢., clases nO abstractas), deberdn afiadir una implementaci6n particular a cada método, Escaneado con CamScanner 3.2 Implementacién de interfaces 41 tal como se muestra en el cédigo representado a continuacién: 2 » n » public class Hospital implements Visitable{ private String horario; public String getHorarioVisita()( return horario; } public void setHorarioVisita("Lu-Do, 9:30-19:00"){ rario = "Lu-Do, 9:30-19:00"; public class Zoologico implements Visitable( e String horario; ing getHorarioVisita(){ en horario; pur void setHorarioVisita("Sa-Do, 12:00-19:00"){ = "Sa-Do, 12:00-19:00"; public class MonumentoHistorico implements Visitable{ private String horario; public String getHorarioVisita (){ return horario; public void setHorarioVisita("Lu-Vi, 10:00-1 horario = "Lu-Vi, 10:00-18:30"; 230") En la versién 8 de Java se integra la posibilidad de implementar mé- todos por defecto en Jas interfaces (identificados mediante el modificador ‘a permite que las clases hereden el método implementado en la interfaz sin necesidad de incluir el mismo c6digo en cada clase. Obviamente para que esta caracterfstica resulte de utilidad e] comportamiento de los métodos de la interfaz deber ser valido para todas las clases, o al menos para la inmensa mayorfa, de forma que aquéllas que lo deseen puedan sobreescribir el método si no les resulte de utilidad la implementaci6n heredada por defecto. En el contexto de nuestro Escaneado con CamScanner 42 Herencia ejemplo de motivacién, imaginemos que una ordenanza municipal estable- ce que el horario de visita de todas las instalaciones pblicas de la ciudad (incluidas el z00, los hospitales y los monumentos hist6ricos) sea de lunes a sébado de 10:00 a 18:00. En este supuesto, tiene sentido redefinir la in- terfaz Visitable de nuestro ejemplo como se muestra a continuacién (en lugar de replicar exactamente el mismo extracto de cédigo en tres clases). public interfaces Visitable( default String getHorarioVisita(){ return "Lu-Sa, 10:00-18:00"; } Los métodos default no sélo impiden tener que afiadir una misma implementacién en cada clase, sino que también permiten extender las in- terfaces de forma transparente para las clases que las implementan. Si al ex- tender una interfaz slo pudiésemos ajiadir nuevos métodos abstractos, las clases instanciables que implementaban dicha interfaz antes de los cambios deberdn incorporar esos nuevos métodos, con sus respectivas codificacio~ nes, para evitar asf convertirse en clases abstractas. Por el contrario, si los métodos de la interfaz son heredados con una implementaci6n por defecto, la extensién de la interfaz no supone cambio alguno en las clases. Ademés de los métodos default, Java 8 también permite definir ¢ implementar métodos estaticos en las interfaces (identificados mediante el modificador static), los cuales no pueden ser sobreescritos (recordar las restricciones descritas en seccién 3.1.2) y deben ser invocados necesaria- mente a través del nombre de la interfaz, Ademis de los métodos abstract, default o static, en una interfaz se pueden definir constantes (marcadas con los modificadores final static en el ejemplo mostrado a continua ciGn), que serén heredadas por aquellas clases que desen utilizar estos va- Jores en sus atributos o en el cuerpo de sus métodos, | public interface listarteses| public static final int enero = 1, febrero = 2, } marzo = 3, abril = 4, mayo = 5, junio = 6, julio = 7, agosto = @, septiembre = 9, + octubre = 10, noviembre = 11, diciembre = 12; ) Public class Meses implements listarMeses { Escaneado con CamScanner 3.2 Implementacién de interfaces 4a 9 public static void main (String[] args){ System.out.println("Marzo es:" + marzo); " , De la misma forma que el programador puede definir tantas interfaces propias como considere itil en el dominio de cada sistema software, existen ‘otras muchas que la API de Java pone a disposicién de los desarrolladores. Una de las més cominmente adoptadas es la interfaz Comparable que, co- mo su nombre indica, permite comparar dos objetos de un mismo tipo para detectar cudl de ellos es mayor 0 incluso, de ser el caso, si son iguales. Enel ejemplo mostrado a continuacién comparamos dos personas dadas segin su edad, dcshaciendo los empates mediante sus respectivos nimeros de DNI. interface Comparable ( compareTo (Object 0); | public class Persona implements Comparable { private int edad; 3} private String DNI; s public Persona(int edad,String dni) { this.edad = edad; 7 DNI = dni; } public int compareTo (Object 0){ " if (edad < ((Persona)o).edad) return -1; else if (edad > ((Persona)o).edad) return 1; 3 else{ return DNI.compareTo (((Persona)o) .DNI); Is ) ) Public String toString (){ ” return "DNI: " + DNI + " - " + "Edad: " + edadi } a) J Escaneado con CamScanner 44 Herencta El] método compareTo establece en realidad el orden natural de los ob- jetos de ta clase que implementa Ja interfaz, de ahf que sea el criterio por defecto que se asume cuando queremos ordenar una colecci6n de elementos sirviéndonos de las utilidades que la API de Java proporciona a tal efecto, En concreto, el método sort. de la clase Col lect Jons permite ordenar una colecci6n dada utilizando por defecto el criterio implementado en el méto- do compareTo de la interfaz Comparable. Sirva como ejemplo el siguiente extracto de cédigo donde se presenta por pantalla una lista ordenada de cuatro personas (en orden creciente de edad y recurricndo al DNI en caso de empate), utilizando el método toString () sobreescrito en la clase para mostrar los atributos de cada una de cllas, , s 1s ” Amport java.util, ArrayList; Amport java.util.Collections; Public class Ordena ( public static void main (String args(])( ArrayList lista = new ArrayList (); lista.add (new Persona (38, "444583853")); lista.add(new Persona (13,"323456780")); lista.add (new Persona (38,"34352135K")); lista.add (new Persona (9,"77864973L")); Collections. sort (lista); for (int i=0;i> Podables Kt + getEstacionPodadoj): String Figura 3.2: Disefio UML adoptado en el sistema de gestién del herbolario. Descrfbase el escenario modelado en dicho diagrama identificando los principales componentes y comportamientos definidos mediante el paradig- ma de POO, e incliyase la codificacién Java correspondiente. Solucién El disefio propuesto modela una jerarquia de clases en las que se iden- tifican cuatro tipos diferentes de Plantas: Arboles, Arbustos, Hierbas y Matas. Las dos primeras son especies que se deben podar, mientras que las dos tiltimas no requieren tal cuidado. No se han definido atributos es- Escaneado con CamScanner 58 Herencia pecfficos en cada clase de planta, modelando nicamente caracteristicas re. feridas a su altura, origen y lista de Clientes que la han comprado, Ademés, dado que Plantas es una clase abstracta en el sistema s6lo se po- dran instanciar sus cuatro clases hijas, las cuales comparten el tipo comin (Plantas) heredado del padre. La codifi los Arboles se podan en continuacién: ci6n Java del escenario descrito (en el que se ha asumido que invierno y los arbustos en primavera) se muestra a 2 } 4 6 8 } 2 2» } n u % * } public interface Podables{ String getEstacionPodado(); public abstract class Plantas{ private double altura; private String origen; public ArrayList compradores = new ArrayList (); public class Arboles extends Plantas implements Podables{ public String getEstacionPodado() { return "Invierno"; } ) 6 public class Arbustos extends Plantas implements Podables{ public String getEstacionPodado() { return "Primavera"; ) public class Matas extends Plantas{ public class Hierbas extends Plantas{ Notese que la relacién entre Plantas y Clientes se ha modelado co- Escaneado con CamScanner 3.3 Cuestiones de revision 59 mo un atributo de la primera clase de tipo ArrayList, dado que, a la vista de la cardinalidad miltiple indicada en el diagrama UML, es necesario po- der registrar un ntimero no acotado de compradores en cada especie. Cuestién 9 Asumiendo la jerarquia de clases modelada en la solucién de la cuestion 8, incorpore tantos métodos toString() como considere oportunos para que éstos puedan acceder a los valores de los atributos altura y origen de cada tipo de Planta del herbolario. Solucién Dado que el método toString () tnicamente debe acceder a los atribu- tos que comparten todas las Plantas, nuestra solucién debe sobreescribir dicho método tnicamente en esa clase. Las cuatra hijas heredardn esa ver- si6n directamente, la cual es valida para todas ellas por no necesitar acceder a sus atributos particulares (no contemplados en el disefio UML ilustrado en Fig. 3.2). public abstract class Plantas{ private double altura; private String origen; private Arraylist compradores = new ArrayList (); public String toString(){ return "Altura: " + altura + " ~ Origen: " + origen; Cuestién 10 Implemente el cédigo necesario en las clases Profesor, Alumno y Perso- na mostradas en el diagrama UML de la Figura 3.3 para que puedan eje- cutarse correctamente las sentencias del método main de la clase Prueba, mostrando la siguiente salida por pantalla: Escaneado con CamScanner Here Nombre: Sofia Garcia Lopez Edad: 20 Nota media: 8.5 Nimero de asignaturas en matricula: 11 | public class Prueba{ public static void main (String[] args){ Persona pl = new Profesor ("Mario Macias Gémez* /40,"Vi, 10 a 12h.", 12); Persona p2 = mew Alumno ("Sofia Garcia Lépez* 120,8.5,1107 system.out .print1n(p2); Persona nombre: String ~edad: Profesor Alumno ~tutorias: String -nota_media: double -carga_docente: int | | asignaturas_matricula: int Figura 3.3: Herencia entre las clases Alumno, Profesor y Persona. Solucién En primer lugar, para poder crear los objetos p1 y p2 es necesario afia- dir los métodos constructores correspondientes en las clases Profesor y Alumno (para la inicializaci6n de los atributos relativos al horario de tuto- rfas y la carga docente del profesor, asf como de la nota media y la matricula del alumno), amén del constructor de Persona (en lo tocante a su nombre y edad). Asimismo, en la Ifnea 5 de Prueba se hace una llamada implicita al método toString el cual deberd ser correctamente sobreescrito (en las cla- ses Alumno y Persona) para presentar Ia informacién en el formato exigido Escaneado con CamScanner 3.3 Cuestiones de revision 61 en el enunciado de la cuestién, Nétese la invocaci6n desde Aluno al mé. tego toSt ring) de Persone (Iinea 37) para poder acceder a los atributos (privados y por ende invisibles) del padre (nombre y edad). “public abstract clas private string nort private int edad; tring n, dint e){ ing ({ + nombre + "\n" + "Edad: * + \ + public class private private double public 7 public class Alumno extends Persona( \7 double nota_media; int asignaturas_matricula; Public Alumno (String n, dnt e, double nota, int mat Mu ¥ super (n,é); nota_media = nota; » asignaturas_matricula = mat; » Public String toString (){ * return super.toString() + "Nota media; " + Escaneado con CamScanner H 62 Escaneado con CamScanner Capitulo 4 Polimorfismo En este capitulo detallaremos los mecanismos que sientan las bases de la programacién polimérfica, asi como las directrices de las conversiones de tipo que conlleva este proceso. Tras ejemplificar la programacién con genéricos mediante la parametrizacién de tipos, concluiremos el capitulo resolviendo una seleccién de cuestiones en las que se ilustraré la conviven- cia del polimorfismo con las relaciones de herencia y las interfaces. 4.1. Introduccién En la introducci6n de este libro ya revelabamos el origen etimoldgi- co del término polimorfismo, que en latin significa multiples morfos. En el contexto particular de la POO, las multiples formas o tipos que tiene un objeto se gestan a través de los mecanismos de herencia, ya sea mediante relaciones de generalizacién-especializacién o a través de la implementa- cién de interfaces. Programar de forma polimérfica exige, pues, explotar el tipo comin a un conjunto de clases, que ser el de una superclase o el de la interfaz que implementan todas ellas. En ambos supuestos se persiguen c6- digos compactos y reutilizables, vélidos a medida que se incorporan nuevas clases como consecuencia de la extensién de un sistema. La premisa de la generalidad se sustenta en el hecho de explotar en la programacién el tipo genérico de la superclase o de Ja interfaz, que ser comtin a todas las clases especificas que extienden de ese padre o que implementen dicha interfaz. 63 Escaneado con CamScanner 64 Polimorfismo 4.2. 4Cémo funciona el polimorfismo? EI polimorfismo exige utilizar dos tipos de cada objeto: el tipo de la clase que se ha instanciado para crearlo (invocando su constructor a través del operador new), denominado tipo real, y cl tipo que ese objeto comparte con otros, denominado tipo declarado, que seré el de una superclase o el de una interfaz implementada por su tipo real. Esta dualidad permite definir sendos objetos de la clase Circulo, tal como se muestra a continuacién, en los que se explota como tipo declarado la superclase FigGeo y la interfaz Comparable: 0 objetol = new Circulo(); Comparable objeto2 = new Circulo (negro, true, 2.7); Si recordamos el cédigo mostrado en el capitulo anterior, tanto la super- clase FigGeo como la clase Circulo sobreescribfan el método toString () para poder obtener una representacidn de sus objetos como cadenas de tex- to, de manera que disponemos de dos morfos diferentes para dicho método. Esta situaci6n plantea la siguiente disyuntiva: {Qué método se ejecuta si invocamos objet ol.toString()? {El alojado en la clase correspondiente al tipo real Circulo, bien el ubicado en la clase del tipo declarado FigGeo? Para resolver este dilema es preciso presentar los dos mecanismos que vertebran la programacién polimérfica: la comprobacién de signatura (met- hod matching) que permite buscar un método en tiempo de compilacién pa- ra asegurar que su invocacién es valida, y la vinculacién dindmica (dynamic binding) que se basa en enlazar la implementacién del método en tiempo de ejecucién. «= Enel tiempo de compilacién, el compilador busca en la clase relativa al tipo declarado del objeto un método cuya signatura encaje con el némero, tipo y orden de los pardmetros usados en la invocacién (i.¢.. Escaneado con CamScanner (én de tipos de datos 65 se busca en FigGeo un método Hamado toString que retorne una cadena y no reciba pardmetros). En el tiempo de ejecucién, 1a maquina virtual Java (JVM) enlaza la implementacién del método de forma dinémica considerando el tipo real del objeto. En concreto, la JVM busca en la clase relativa al tipo real del objeto (Circulo) un método cuya signatura coincida con la definida en el tipo declarado (FigGeo). Si dicho método existe en la clase relativa al tipo real, se ejecuta ese cédigo; en caso contrario, se sube un nivel en la jerarqufa y se repite la comprobacién. Este proceso terminaré al alcanzar la clase del tipo declarado, en la que el compilador ya ha comprobado que reside el método en cuesti6n. Del funcionamiento descrito se desprende que la sobreescritura de un método exige que se respete no s6lo su nombre, sino también su valor de retorno, lista y tipo de pardmetros de entrada. De lo contrario, en lugar de ejecutar el método que el programador ha sobreescrito en el tipo real, la vinculacién dindmica ejecutaré el cédigo del método alojado en el tipo declarado. Esto ultimo no tendria ningtin sentido toda vez que si el método heredado (del tipo declarado) fuese valido en el tipo real, el programador no lo habria sobreescrito en esa clase. 4. . Conversidn de tipos de datos Trabajar con dos tipos de un mismo objeto, diferentes en tiempos de compilacién y ejecucién, conlleva realizar ciertas conversiones (de tipos de datos) acufiadas en Java con el término anglosajén casting. En realidad, podemos hablar de dos tipos de casting: = Un casting implicito que es prescindible debido a las relaciones de herencia que subyacen de Ia existencia de una superclase o de la implementacién de una interfaz, Por ejemplo, las conversiones a los tipos FigGeo y Comparable son innecesarias en las asignaciones mostradas a continuacién, toda vez que los cfrculos son figuras geo- métricas(class Circulo extends FigGeo) que se pueden compa- rar entre sf(class Circulo implements Comparable). FigGeo objetol = new Circulo(); 4 Comparable objeto2 = new Circulo()i Escaneado con CamScanner

You might also like