PROG06 Programación Avanzada

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 21

66 RESUMEN UNIDAD DIDÁCTICA:

PROGRAMACIÓN AVANZADA

Contenido:

1. Herencia _________________________________________________ 2
1.1. Conceptos _____________________________________________ 2
1.2. Implementación en Java _________________________________ 6
1.3. Sobrecarga y anulación de métodos _______________________ 6
1.4. Constructores __________________________________________ 7
1.5. Métodos finales ________________________________________ 8
1.6. Casting e instanceof ____________________________________ 9
2. Clases abstractas _________________________________________ 11
2.1. Métodos abstractos ___________________________________ 11
2.2. Clases abstractas ______________________________________ 13
3. La clase Object ___________________________________________ 14
3.1. Concepto _____________________________________________ 14
3.2. Método equals ________________________________________ 14
3.3. Método toString ______________________________________ 15
4. Interfaces Java ___________________________________________ 17
4.1. Concepto _____________________________________________ 17
4.2. Interface Comparable __________________________________ 20
1. Herencia
1.1. Conceptos
Sabemos que el Diagrama de Clases es el principal medio en UML para describir el
diseño del sistema.

Ejemplo de Asociación
PR

Ejemplo de Agregación

Ejemplo de Composición

La relación de Herencia se da entre dos clases cuando una de ellas representa una
especialización con respecto a la otra.
La clase especializada (clase “hija”) poseerá las características (métodos y
atributos) comunes que proceden de la clase general.
Ejemplo 1
Si diseñaras una clase que represente Animales, ésta podría tener como
operaciones: respirar, reproducirse o crecer.
Si ahora quisieras una clase para representar Leones, tendrías las mismas
operaciones de antes y algunas propias como correr y rugir.
Lo que hay que hacer NO ES definir de nuevo los métodos comunes, sino
simplemente indicar que un león es un animal.
Gracias a la herencia se consigue:
ESTRUCTURAR de forma elegante el diseño del sistema
REUTILIZAR el código de forma más eficiente
De esta forma se pueden crear nuevas clases partiendo de una clase ya existente.
Este tipo de relación es transitiva.
La necesidad de crear una nueva clase viene dada porque la clase existente no nos
permite crear objetos del modo que queremos.
Nuestra clase sí permitiría crear dichos objetos y por tanto, me aprovecho de la
clase existente pero la “personalizo” para poder crear el tipo de objeto que deseo.
Ejemplo 2
Tenemos implementada la clase Coche y ahora necesitamos la clase Motocicleta.
Como se puede ver, tienen prácticamente las mismas propiedades y operaciones.
NO sería práctico ni recomendable implementar todos los métodos de nuevo para
la clase Motocicleta.
PR

La solución sería crear una clase general Vehículo que contenga todas las
propiedades y operaciones comunes.
Además, para crear una clase nueva llamada Tractor, solo tendríamos que
indicar que hereda de Vehículo.
1.2. Implementación en Java
El API de Java es un conjunto de clases organizado jerárquicamente
mediante herencia.
Incluso las clases que nosotros creemos, son subclases de la clase Object.
En Java, la herencia se indica mediante la palabra clave extends.
public class Coche extends Vehiculo{
...
}
Los atributos y métodos cuyo modificador de acceso sea private o package
NO son heredados.
Para que puedan ser heredados deben usar el modificador de acceso
protected o public.
El modificador protected permite que el miembro sea heredable pero es
como el private, manteniendo la ocultación al utilizarlo en los atributos.
Además será normal que la subclase agregue sus atributos y métodos
propios.
1.3. Sobrecarga (Override) y anulación de métodos
Sabemos que las subclases heredan métodos de las superclases. En la subclase
tengo las siguientes posibilidades:
Implementar nuevos métodos que “especialicen” el comportamiento de la
subclase.
Sobrecargar métodos heredados. Tienen que tener exactamente la misma
cabecera.
Anular algún método heredado.
➢ Sobrecarga. Un ejemplo claro son los constructores de la clase
Será un nuevo método con el mismo nombre que uno heredado, pero con
distintos parámetros.
De esta manera puedes proporcionar una variante de un determinado
método, adaptado a las necesidades de la nueva clase.
PR

➢ Anulación. Un ejemplo claro es el método toString() heredado de Object


Para anular un método heredado, debes implementar un método con el
mismo nombre, tipo y argumentos que un método de la superclase.
Se dice entonces que la subclase sobreescribe el método.
Es la forma que tiene la subclase de cambiar el comportamiento con
respecto a la superclase en algunas operaciones.
Ejemplos
public class Pez extends Animal{
public void respirar(){
// Método sobreescrito (anula al heredado)
}
}
public class Motocicleta extends Vehiculo{
public void acelerar(){
// Método sobrescrito (anula al heredado)
}
}

1.4. Constructores
Los constructores NO se heredan.
Cada clase debe proporcionar sus propios constructores. Lo que sí hay que tener en
cuenta es que:
Al crear un objeto de una clase, siempre se invoca primero el constructor
por defecto de la clase padre y después el suyo propio.
public class Motocicleta extends Vehiculo{
// Constructor sin parámetros
public Motocicleta(){
// Llamada implícita al constructor Vehiculo()
}
}
La llamada al constructor de la superclase siempre se realiza, incluso si no
has definido ningún constructor en tu subclase.
Si se desea invocar a un constructor diferente al de por defecto, se debe
realizar con la palabra reservada super.
En este caso es obligatorio usar super (marca) porque si no, llamaría
implícitamente al constructor sin parámetros con super().
public class Motocicleta extends Vehiculo{
// Constructor con 1 parámetro
public Motocicleta(String marca){
// Llamada constructor Vehiculo(marca)
super(marca);
}
}
➢ Reglas
Si la primera instrucción de un constructor de una subclase NO es una
invocación a otro constructor con this o super, Java añade de forma
invisible e implícita una llamada super() con la que invoca al constructor
por defecto de la superclase.
Luego continúa con las instrucciones de inicialización de los atributos de la
subclase.
Si en la superclase no hay constructor por defecto (sin parámetros),
ocurrirá un error en la compilación.
Si se invoca a constructores de superclases mediante super(…) en la
primera instrucción, entonces se llama al constructor seleccionado de la
superclase, luego inicializa los atributos de la subclase.
Si esa primera instrucción es una invocación a otro constructor de la clase
con this (…), entonces se llama al constructor seleccionado por medio de
this y realiza sus instrucciones.
El uso de super y this NO puede ser simultáneo puesto que ambos tienen
que ser la primera instrucción. Lo que significa que hay que elegir entre
ambas.
Ejemplo: https://www.arquitecturajava.com/java-constructores-y-super/
1.5. Métodos finales
Sabemos que una subclase puede:
sobrecargar métodos herededados
sobreescribir y anular métodos herededados
Pero una superclase puede estar diseñada para que algunos métodos siempre se
hereden en las subclases y por tanto NO puedan ser sobreescritos.
En este caso, en la superclase se habrá empleado la palabra final en la definición
del método.
PR

Ejemplo
public class Vehiculo {
// Método que puede ser sobreescrito
public void acelerar(int cantidad){
this.velocidad+=cantidad;
}
// Método que NO puede ser sobreescrito
public final void frenar(int cantidad){
this.velocidad-=cantidad;
}
}
1.6. Casting e instanceof
El casting es una conversión entre tipos compatibles. Por ejemplo entre int-char o
int-double.
Cuando tenemos dos objetos de clases distintas (son tipos distintos), el casting
entre ellos NO es posible, a no ser que tengan una relación de herencia.
Lo que consigues con la herencia es poder almacenar en una referencia de una
superclase (ejemplo Vehiculo), un objeto de una de sus subclases (Coche).
Al revés no sería posible.
Ejemplo
// Creo una referencia al objeto Vehiculo
Vehiculo refV = null;
// Creo dos objetos: un Coche y una Motocicleta
Coche unCoche = new Coche ("Audi", 5);
Motocicleta unaMoto = new Motocicleta();
// UNA referencia de la superclase puede almacenar la
// referencia a un objeto de la subclase
refV = unCoche; // un coche "ES UN" Vehiculo
//unaMoto = refV; // ERROR!!!! Un objeto subclase no puede
// almacenar la referencia de un objeto de la
// clase madre
Ten en cuenta que los objetos nunca cambian de tipo. En el ejemplo, con refV solo
se podrá invocar métodos propios de la clase Vehiculo, no de Coche, aunque
guarde la referencia a un Coche.
refV.acelerar(10);
refV.frenar(20);
refV.activarEPS(); // ¡ERROR! Por ser un método de Coche y no
// de Vehiculo
Uso de instanceof
Permite comprobar si un determinado objeto pertenece a una clase concreta
objeto instanceof clase
Imagina que tenemos un método que acepta como parámetro un objeto Vehiculo.
private static void diagnosticar(Vehiculo vv){
...
}
El parámetro podrá ser un Vehiculo o cualquier objeto de una subclase de
Vehiculo (un Coche, una Motocicleta, etc.) porque dicho objeto es un
Vehiculo.
Dentro del método, seguramente queramos manejar el objeto pasado e
invocar a sus métodos propios. Para ello necesitas hacer el casting.
// Supongo que es un coche, por lo que se convierte a Coche
Coche refCoche = (Coche) vv;
// Ahora se puede llamar a un método propio de Coche
refCoche.activarEPS();
Si el objeto pasado NO es un Coche, el casting produciría la Excepción
ClassCastException.
En general, para evitar este problema, antes de hacer un casting, debemos
comprobar qué tipo de objeto ha llegado en el parámetro. Esta operación
se hace con instanceof
if (vv instanceof Coche){
// Ahora puedo hacer CASTING con seguridad
Coche refCoche = (Coche) vv;
// Llamada a un método propio de Coche
refCoche.activarEPS();
}
else if (vv instanceof Motocicleta) {
// Ahora puedo hacer CASTING con seguridad
Motocicleta refMoto = (Motocicleta) vv;
}
Si se comprueba un objeto de una clase hija de la indicada usando
instanceof, el resultado será true.
En el ejemplo de arriba, si vv contiene un objeto cuya clase es hija de
Coche, la condición se evaluaría a cierta.
PR

2. Clases abstractas
2.1. Métodos abstractos
Observa el ejemplo:
private static void diagnosticar(Vehiculo vv){
if (vv instanceof Coche){
// Ahora puedo hacer CASTING con seguridad
Coche refCoche = (Coche) vv;
// Llamada a un método propio de Coche
refCoche.activarEPS();
}
...
}
Sabemos que mediante la referencia vv, solo se podrá llamar a métodos definidos
en la superclase.
Cuando una operación NO se puede implementar en la superclase porque es algo
más específico, debe implementarse en la subclase y para ser llamado debe
utilizarse instanceof.
Ese método específico, se podría implementar (o no) en todas las subclases, y en
cada una de ellas podría tener diferente nombre y/o parámetros, lo cual
dificultaría la llamada y habría que distinguir cada caso.
Ejemplo: calcularArea()
Si suponemos que el cálculo del área de un polígono regular es específico
de cada polígono, este método solo se puede implementar en las
subclases (Cuadrado, Triángulo, etc).
En un proceso donde se llame a dicho método, sería necesario saber qué
objeto es y qué método lo hace.
public static double sumaAreas(PoliRegular polis[]){
double suma=0.0;
Cuadrado refCua;
for (int i=0;i<polis.length;i++){
if (polis[i] instanceof Cuadrado){
refCua = (Cuadrado) polis[i];
// Es un Cuadrado, calculo su área
suma = suma + refCua.calcAreaCuadrado();
}
}
return suma;
}
Esta implementación es bastante problemática: un caso para cada tipo de
objeto.
¿Cuál sería la solución?
La solución sería que en todas las subclases de PoliRegular
existiera el método para calcular su área, y
se llamase de igual forma
public static double sumaAreas(PoliRegular polis[]){
double suma=0.0;
for (int i=0;i < polis.length;i++){
// Sabemos que todos los objetos
// que son PoliRegular (subclases),
// tienen el método calcularArea()
suma = suma + polis[i].calcularArea();
}
return suma;
}
La superclase puede obligar a las subclases a implementar un
determinado método con un nombre y una lista de parámetros.
Este método estará definido en la superclase como un método
abstracto, es decir, define un método teórico sin implementación en
la superclase, que DEBE IMPLEMENTARSE en TODAS las subclases
porque se hereda vacío.
Si sabes que todas las subclases implementan un método concreto,
podrás realizar métodos genéricos para objetos de la superclase, sin
importar qué objetos concretos son, puesto que sé que tienen el
método que necesito.
Ejemplo:
/**
* Método ABSTRACTO, definido en PoliRegular
* que calcula el área del polígono.
* DEBE ser implementado en cada subclase de forma
* específica para cada polígono.
* @return el área del polígono.
*/
abstract public double calcularArea();
PR

2.2. Clases abstractas


Definir un método abstracto en una clase, obliga a que éste sea implementado en
cualquier subclase.
Un método abstracto en una clase, hace que dicha clase sea abstracta.
Las clases que poseen algún método abstracto se les llama clases abstractas. Son
clases creadas para servir de clases base para herencia, de modo que la utilidad la
tengan cuando se desarrolla una subclase.
Una clase abstracta debe ser marcada con la palabra clave abstract.
Cada método abstracto de la clase, también llevará la palabra abstract.
NO se pueden crear objetos de una clase abstracta.
Ejemplo
abstract public class PoliRegular{
protected float medidaLado;
protected int numLados;

/** Este método es el mismo para todas las subclases de


* PoliRegular por estar declarado como final. No se
* puede implementar en las subclases
*/
public final int calcularPerimetro(){
return this.medidaLado * this.numLados;
}

/** Definición del método abstracto que será obligatorio


* definir en cada uno de los polígonos que hereden
* de la clase PoliRegular
*/
abstract public double calcularArea();

} // Fin de la clase PoliRegular

public class Cuadrado extends PoliRegular{


...
// método para calcular el área de un Cuadrado
public double calcularArea(){
return Math.pow(this.medidaLado, 2);
}
} // Fin de la clase Cuadrado
3. La clase Object
3.1. Concepto
La clase Object es la superclase común de TODAS las clases Java, las del API y las
creadas por cualquier desarrollador.
Esto significa que cualquier objeto Java es un Object.
Esta característica, como se ha visto antes, permite crear métodos genéricos cuyo
parámetro sea un objeto de cualquier tipo:
public static void metodoG(Object o){
...
}
A este método se le puede pasar cualquier clase de objeto.
¡Cuidado! tendrás que tener en cuenta muchas cosas para que funcione de forma
correcta la implementación del método.
Observa que las siguientes llamadas serían válidas:
int a =9;
String str = "Hello";
Cuadrado cua = new Cuadrado(9);
metodoG(a);
metodoG(str);
metodoG(cua);
Métodos de Object
Otra particularidad de la clase Object, es que proporciona una serie de métodos
interesantes para las subclases. Es decir, hay métodos que TODAS las clases Java
poseen porque los heredan de la clase Object.
Lógicamente, es necesario que estos métodos sean sobreescritos en la subclase
para adaptarlos a ella y que funcionen correctamente.
Entonces, ¿cuál es su utilidad?
Lograr que todas las clases utilicen el mismo nombre y parámetros para
operaciones usuales como comparar, clonar, etc.
3.2. Método equals
Este método está en la clase Object y sirve para comprobar si dos objetos son
iguales.
Recibe como parámetro un objeto con quien comparar.
Devuelve true si el objeto con el que llamó y el pasado son iguales;
devolverá false en caso contrario.
PR

La implementación de este método en la clase Object es:


public boolean equals(Object obj) {
return (this = = obj);
}
Lo único que hace es comprobar que las referencias de los dos objetos son iguales.
➢ Veamos cómo se sobrescribiría el método
En tu clase lo sobreescribirás para establecer el criterio de igualdad entre dos
objetos.
Ejemplo
public boolean equals(Object obj){
// variable bandera
boolean iguales;
// variable con la que comparar
PoliRegular otro;
// Compruebo si la ref. es null,
// o si NO “es un” objeto PoliRegular
if (obj == null || !(obj instanceof PoliRegular){
iguales = false;
}
// variable para comprobar si son iguales o no
otro = (PoliRegular) obj;
// ¿Tienen el mismo nº de lados y estos miden lo mismo?
if ((this.getMedidaLado () == otro.getMedidaLado()) &&
this.getNumLados() == otro.getNumLados())
iguales = true;
}
return iguales;
}

3.3. Método toString


Este método está en la clase Object y sirve para obtener una cadena que describa
la información del objeto.
La implementación de este método en la clase Object es:
public String toString() {
return getClass().getName() + "@" +
Integer.toHexString(hashCode());
}
➢ Veamos cómo se sobrescribiría el método
Al sobreescribirlo en tu clase, lo normal es componer una cadena con el valor de
los atributos más importantes del objeto.
Ejemplo
public String toString(){
String str ="\nPoligono regular de lado: " +
this.getMedidaLado();
return str;
}
Ejemplo: Se redefine el método en la clase Coche
public String toString(){
String str =" \nVelocidad:" + this.getVelocidad() +
"\nGasolina: " + this.getGasolina()
return str;
}
PR

4. Interfaces Java
4.1. Concepto
Los interfaces son el mecanismo que utiliza Java para suplir la herencia múltiple.
Mediante la relación de herencia, vimos que si una clase padre define un método
abstracto, todas las clases hijas lo deben implementar (de forma obligatoria).
Cuando necesitamos obligar a que una clase implemente una serie de métodos, NO
es necesario que los herede de una clase que los contenga.
Simplemente, los métodos que queramos que una clase tenga implementados, se
agrupan en un interface (o varios) y se dice que la clase implementa dicho
interface.
Por tanto:
Un interface Java es un conjunto de métodos abstractos.
Un interface define un conjunto de operaciones, o lo que es lo mismo,
define un comportamiento.
Un interface NO define el tipo de objeto que es, sino lo que pueden hacer
una serie de objetos (de clases distintas).
Lo normal es que el nombre de los interfaces terminen con el sufijo
able/ible (Comparable, Configurable, Dibujable, Serializable, Pintable,
Arrancable, Imprimible, etc.).
Se dice que una clase implementa un interface cuando implementa
TODOS sus métodos abstractos.
De esta manera, sabiendo que una clase implementa una serie de métodos, será
posible codificar operaciones que de lo contrario no podría realizar.
Ejemplo: Interface Dibujable
/**
* Define la operación para que algo sea capaz de
* Dibujarse: método dibujar().
*/
public interface Dibujable (){
public void dibujar();
}
Ejemplo : Interface Arrancable
public interface Arrancable(){
protected boolean arrancado = false;
public void arrancar();
public void detener ();
}
Has aprendido ya que puedes tener un método general, cuyo parámetro sea un
objeto de la clase PoliRegular e invocar a un método que sabes que tiene cualquier
objeto que es un PoliRegular.
public static void metodoG(PoliRegular poli){
...
valor = poli.calcularArea();
}
Pues ahora vas a ver que también puedes tener un método general cuyo parámetro
sea un Interface, de modo que se podrá pasar un objeto de cualquier clase que
implemente dicho Interface (NO hace falta que tengan una clase padre común).
public class EjemploApp {
public static void mostrar(Dibujable forma){
forma.dibujar();
}
}
La clase Cuadrado implementa el Interface Dibujable y lo indica con la palabra clave
implements.
public class Cuadrado extends PoliRegular implements Dibujable {
/**
* Método abstracto de la clase PoliRegular, por eso es
* obligatorio implementarlo en la subclase Cuadrado
*/
public double calcularArea(){
return Math.pow(this.getMedidaLado(), 2);
}
/**
* Método del interface Dibujable, por eso, si se quiere
* utilizar es obligatorio implementarlo en la subclase
* Cuadrado
*/
public void dibujar(){
System.out.println (“Se dibuja un Cuadrado”);
System.out.println (“-----------”);
System.out.println (“- -”);
System.out.println (“- -”);
System.out.println (“-----------”);
}
}
La clase Muñeco es algo muy diferente a un PoliRegular, pero podría también
implementar el Interface Dibujable.
PR

Dicha clase tendría que implementar su propio método dibujar().


public class Muñeco implements Dibujable {
public void dibujar(){
System.out.println (“Se dibuja un Muñeco”);
System.out.println (“( * * )”);
System.out.println (“( o )”);
System.out.println (“( - )”);
}
}
Con los interfaces hacemos que objetos muy distintos puedan tener un
comportamiento común (operaciones comunes), aunque realice algo distinto en
cada caso.
public class EjemploApp {
/**
* Llamo a metodoG con cualquier objeto Dibujable.
*/
public static void otroMetodo(){
Dibujable objeto;
Cuadrado cua1 = new Cuadrado(5);
objeto = cua1;
// mostrar recibe un objeto Dibujable
mostrar(objeto); // muestra un cuadrado
Muñeco pato = new Muñeco(“Tico”);
objeto = pato;
mostrar(objeto); // muestra un muñeco
}
}
Como puedes ver, se puede usar una referencia a un Interface como si fuera una
referencia a un objeto de una Clase, siempre y cuando la clase del objeto que
referencie implemente dicho Interface.
Dibujable objeto;
Cuadrado unCuadrado = new Cuadrado(5);
Muñeco unMuñeco = new Muñeco(“Bear”);
objeto = unCuadrado;
objeto.dibujar();
objeto = unMuñeco;
objeto.dibujar();
También es posible utilizar el operador instanceof con un interface.
Bloque unBloque = new Bloque (GREEN_COLOR);
if (unBloque instanceof Dibujable){
// Si implementa el interface D i b u j a b l e
objeto = unBloque ;
objeto.dibujar();
}
Ejemplo de interface Operable usado por la clase Tren

4.2. Interface Comparable


Si observas la documentación del API de Java, verás que junto con los nombres de
las clases, aparecen varios Interfaces. Se distinguen porque aparecen en letra
cursiva.
Dos de ellos son Serializable y Comparable. El primero se verá en el tema de
ficheros; veamos ahora el segundo.
PR

La documentación del método sort, dentro de la clase Arrays, dice lo siguiente:


public static void sort(Object[] a)
“All elements in the array must implement the Comparable interface”
Es decir:
Al método sort se le puede pasar un array cuyos elementos sean de
cualquier tipo; un tipo simple o un objeto de ¡¡¡cualquier clase!!!!
El único requisito es que la clase debe implementar el interface
Comparable.
El interface Comparable solo posee el método siguiente:
int compareTo (T otroObjeto)
devuelve:
0 si son iguales
valor > 0 si con el que se invoca es mayor que el otroObjeto pasado como parámetro
valor < 0 si con el que se invoca es menor que el otroObjeto pasado como parámetro
Si una clase implementa este método, podrá utilizar el método sort para ordenar
sus objetos.
Ejemplo: Clase Cuadrado
Se podrían ordenar los objetos por la medida del lado.
public class Cuadrado extends PoliRegular
implements java.lang.Comparable<Cuadrado>{
public int compareTo(Cuadrado otroCua){
int este = this.getMedidaLado();
int otro = otroCua.getMedidaLado();
int res = 0;
if (este < otro){
//Si el que pasamos tiene el lado mayor,
//el resultado será menor que 0
res = -1;
}
else {
//sino, con el que se invoca al método será mayor
res = 1;
}
/* si no es ni mayor ni menor es porque son
iguales, entonces res tendrá valor 0 porque
no lo habrá cambiado desde la inicialización*/
return res;
}}

También podría gustarte