La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Introducción a Java (II)

Presentaciones similares


Presentación del tema: "Introducción a Java (II)"— Transcripción de la presentación:

1 Introducción a Java (II)

2 Índice Introspección Introducción a las relaciones Herencia Agregación

3 Introspección

4 Introducción Java tiene interesantes mecanismos para obtener información interna de las clases, es decir, los objetos Java pueden informarnos de su propia estructura. Por ello se llaman utilidades de introspección Vamos a centrarnos en lo fundamental de la introspección: un objeto o una clase puede informarnos de la clase a la que pertenece La clase más importante es java.lang.Class. Es una clase que nos permite describir cualquier clase Java. Dicho de otra forma, es un descriptor o referencia de una clase. Un paquete relevante es java.lang.reflect

5 Conocer el tipo En tiempo de ejecución puede saber el tipo (clase) que manejamos: Si tenemos un objeto, puedo conocer su clase: Coche miCoche = new Coche(); Class clase = miCoche.getClass(); System.out.println("Clase:" +clase.getName() ); Lo que hemos hecho es obtener un descriptor de clase (tipo Class) mediante miCoche.getClass(). Este descriptor nos devuelve su nombre mediante getName() Si tenemos una clase (no hay instancias): Coche.class.getName() getName() nos devuelve el nombre de la clase, incluyendo la jerarquía de paquetes. En nuestro ejemplo: newInstance.dominio.Coche

6 newInstance() Podemos cargar de manera dinámica un objeto, es decir, determinar en tiempo de ejecución (y no en tiempo de programación) la clase que vamos a instanciar Primero haremos una pequeña clase (Coche) de prueba: package newInstance.dominio; public class Coche extends Vehiculo { private int plazas = 5; public Coche() {} public Coche( int plazas ) { this.plazas = plazas; } public int getPlazas() { return plazas; } public void setPlazas( int p ) { plazas = p; } public String toString() { try { return super.toString() + " Plazas:" + String.valueOf(plazas); } catch (Exception e) { return "-1"; } } A continuación crearemos un objeto del tipo Class, que es un descriptor de la estructura de datos o clase. Lo conseguimos con forName(). El paso siguiente es crear una instancia de la clase con newInstance(). Class clase = Class.forName( “newInstance.dominio.Coche” ); Object objeto = clase.newInstance(); System.out.println(“Coche:" + objeto.toString() ); Necesitamos al menos un constructor sin parámetros (Coche()) para el uso de newInstance() o bien no especificar ninguno. Pero, si se especifica uno que tenga parámetros (Coche( int plazas )), entonces debe también implementar uno sin parámetros, ya que este es el que usa newInstance() Hemos determinado la clase desde una cadena en el código (newInstance.dominio.Coche). Esto no es muy dinámico. Pero las posibilidades de carga dinámica son evidentes: Un ejemplo puede ser que en función de una acción del usuario podemos instanciar una clase u otra. En un archivo properties podemos tener la asociación de acciones y clases. Otro ejemplo: puedo cargar todas las clases que haya en un determinado directorio, sin tener que determinar estáticamente (a priori en el código) las clase que cargo.

7 Introducción a las relaciones

8 Introducción (I) Estamos acostumbrados a ver que diferentes tipos de objetos (naturales o artificiales) se relacionan, por ejemplo en una fabrica, universidad o un despacho de abogados Con las clases ocurre algo parecido, se relacionan entre si. Los dos tipos fundamentales de relaciones son: Relación de clasificación o herencia (jerarquía de tipos). Relación de agregación (jerarquía de partes)

9 Además heredan Marca, Modelo y precio
Introducción (II) La relación de clasificación o herencia es bastante común. Un autobús o un camión son tipos de vehículo, del mismo modo que un ingeniero o un contable son tipos de empleados. Las subclases (como el autobús o el camión) tienen atributos comunes, por el hecho de ser tipos de la clase vehículo. También decimos que heredan atributos de su superclase: Vehículo marca modelo precio ... Camión cargaMax Autobús plazas Además heredan Marca, Modelo y precio Lo que no significa que toda clasificación sea sensata. Borges habla de una enciclopedia china donde “está escrito que los animales se dividen en: a) pertenecientes al emperador b) embalsamados c) amaestrados d) lechones e) sirenas f) fabulosos g) perros sueltos h) incluídos en esta clasificación i) que se agitan como locos j) innumerables k) dibujados con un pincel finísimo de pelo de camello l) etcétera. m) que acaban de romper el jarrón n) que de lejos parecen moscas“ (“El idioma analítico de John Wilkins”, Otras inquisiciones, Emecé Editores, Buenos Aires, 1960, p. 142)

10 Introducción (III) La relación de agregación es también bastante frecuente. Por ejemplo, cuando decimos que: Una empresa se compone de una serie de departamentos El catálogo comercial se compone de una serie de productos La carta de un restaurante se compone de una serie de platos y bebidas Tipos: Composición. La parte (departamento) desaparece cuando desaparece el todo (Empresa). Otro ejemplo: casa/cocina. Contenedor. El componente (Ratón) no desaparece con el contenedor (Computadora). Otro ejemplo: cesta/manzanas. Catálogo Producto Empresa Departamento

11 Herencia La relación en la que una clase base es el supertipo o generalización de una subclase o subtipo (como la clase base Persona y la subclase Librero). Entender los mecanismos de la herencia implica comprender el polimorfismo

12 Un primer ejemplo de herencia
Empezamos representando la relación de herencia entre vehículo y autobus, para una agencia de alquiler: public class Vehiculo { public int precioDia = 36; public String marcaModelo = "Volvo" ; public Vehiculo() { System.out.println( "Construyo un vehículo"); } public void mostrarCaracteristicas() { System.out.print( "Marca: " + marcaModelo + “ Precio: " + precioDia); public class Autobus extends Vehiculo { public int plazas = 55; public Autobus() { System.out.println( "Ha creado un autobus" ); } public void mostrarCaracteristicas() { System.out.print( "Marca: " + marcaModelo + “ Precio: " + precioDia); System.out.println( " Plazas: " + plazas ); public static void main(String[] args) { Autobus v1 = new Autobus(); v1.mostrarCaracteristicas(); } Construyo un vehículo Ha creado un autobus Marca: Volvo Precio: 36 Plazas: 55 Es un primer ejemplo en el que el objeto v1 (clase “Autobus”) hereda los atributos de la clase “Vehículo” (usa la palabra extends): marcaModelo precioDia

13 Algunas críticas El ejemplo anterior mostraba la herencia de atributos, pero tenía varios inconvenientes, entre los que destaca: No aprovecha la herencia de métodos Los atributos no son parametrizados (¿sólo hay Volvos?) Repetimos la sentencia System.out.print( "Marca: " + marcaModelo + "Precio: " + precioDia); en la clase hija Los datos no son protegidos o encapsulados (principio de ocultamiento de información) En resumen, podemos hacerlo mejor

14 Mejorando el ejemplo public class Vehiculo { protected int precioDia;
protected String marcaModelo ; public Vehiculo( String marcaModelo, int precioDia ) { defMarcaModelo( marcaModelo ); defPrecioDia( precioDia ); System.out.println( "Construyo un vehículo"); } public void mostrarCaracteristicas() { System.out.print( "Marca: " + marcaModelo + “ Precio: " + precioDia); public void defPrecioDia( int precioDia ) { this.precioDia = precioDia; ... public class Autobus extends Vehiculo { private int plazas = 55; public Autobus( String marcaModelo, int precioDia, int plazas) { super( marcaModelo, precioDia ); this.plazas = plazas; System.out.println( "Ha creado un autobus" ); } public Autobus( String marcaModelo, int precioDia ) { void mostrarCaracteristicas() { super.mostrarCaracteristicas(); System.out.println( " Plazas: " + plazas ); DESDE MAIN(): Autobus v1 = new Autobus( "Volvo 550", 120, 57); v1.mostrarCaracteristicas(); v1.defPrecioDia( 130 ); Construyo un vehículo Ha creado un autobus Marca: Volvo 550 Precio: 120 Plazas: 57 Marca: Volvo 550 Precio: 130 Plazas: 57

15 Notas al ejemplo En Vehiculo: En Autobus: En main:
Los atributos son protected: esto significa que serán privados para las clases hijas En Autobus: Usamos extends para indicar que es una clase hija de Vehiculo El atributo plazas tiene un valor por defecto de 55, que sería el que tomase con el segundo constructor. Observar que el segundo constructor no recibe valor para esta variable, por tanto, su valor sería el de la inicialización (el 55) El uso de super() nos permite enviar valores al constructor de la clase madre El método mostrarCaracteristicas() sobreescribe o anula el método idéntico de la clase madre. Para acceder al método anulado usa super.: super.mostrarCaracteristicas(); De esta forma el interprete Java sabe que queremos ejecutar la versión de mostrarCaracteristicas() que está en la clase madre En main: Observar la herencia de métodos: el objeto de la clase Autobus llama a defPrecioDia(), método heredado de la clase Vehiculo

16 Los usos de la palabra super
En la forma de llamada a una función significa una llamada al constructor de la clase madre. Debe colocarse en la primera línea del constructor de la hija: Autobus( String marcaModelo, int precioDia, int plazas) { super( marcaModelo, precioDia ); this.plazas = plazas; System.out.println( "Ha creado un autobus" ); } En la forma de referencia a un objeto (super.) significa que llamamos a un método de la clase madre que ha sido sobreescrito: void mostrarCaracteristicas() { super.mostrarCaracteristicas(); System.out.println( " Plazas: " + plazas );

17 Modificadores de acceso y herencia

18 Herencia de datos privados (private)
Si algo es private en la clase madre: NO SE PUEDE ACCEDER DESDE LA SUBCLASE (NO SE HEREDA). Pero si se puede acceder desde la subclase a un método de la clase madre que usa ese dato. Por ejemplo, si hago que el precio sea privado: public class Vehiculo {   private int precioDia;     .... } Entonces puedo mostrar por pantalla el precio PORQUE LO MUESTRA UN METODO HEREDADO DE LA SUPERCLASE, getPrecioDia() : public class j08_agencia_alquiler {   public static void main(String[] args) {     Autobus v = new Autobus( "XXX 800", 140, 58);     System.out.println( v.getPrecioDia() );   } v.getPrecioDia() es un método heredado de la clase madre y es este método el responsable de mostrar el precio por pantalla. Pero desde la clase Autobus (subclase) no puedo usar precioDia, tampoco desde la clase j08_agencia_alquiler, que esta en el mismo paquete 

19 Herencia de datos protected (protegidos)
Si algo es protected en la clase base: SE PUEDE ACCEDER DESDE LA SUBCLASE Y LAS CLASES DEL MISMO PAQUETE (aunque no sean subclases). Ejemplo siguiendo lo anterior: si ponemos: public class Vehiculo {   protected int precioDia;     .... } Las siguientes líneas en main() serían correctas: public class j08_agencia_alquiler {   public static void main(String[] args) {     Autobus v = new Autobus( "XXX 800", 140, 58);     System.out.println( v.getPrecioDia() );     System.out.println( v.precioDia );                    // NO ES UN ERROR   } Puede observarse que el atributo precioDia es accesible para la clase j08_agencia_alquiler (donde está main()), PORQUE ESTA EN EL MISMO PAQUETE.

20 Herencia de datos public (públicos)
Accesible para cualquier subclase o clase (dentro o fuera del paquete). POR DEFECTO: Si no pongo modificador, los atributos y métodos son: Públicos para las clases del mismo paquete o subclases Privados para las clases de otros paquetes o subclases

21 Herencia de datos: un consejo
A modo de resumen y consejo general: Si queremos que algo se herede y se pueda usar desde otro paquete, usar public Si queremos que algo se herede y no se pueda usar desde otro paquete (pero si desde el propio paquete), usar protected Si no se quiere heredar ni acceder, private.

22 Otro ejemplo de sobreescritura de métodos
Las clases hijas (Rectangulo y Circulo) sobreescriben el método getArea(): public class Figura { protected Punto posicion; public void setPosicion(Punto posicion) { this.posicion = posicion; } public Punto getPosicion() { return posicion; } public double getArea() { return 0; } public class Circulo extends Figura { private double radio; static final public double PI = ; public double getArea() { return radio * radio * PI; } public class Rectangulo extends Figura { private double ancho; private double largo; public double getArea() { return ancho * largo; } Cada subtipo de “figura” tiene un método getArea(), pero cada método implementa a su manera el cálculo del área. Por ello se dice que nos encontramos ante polimorfismo: un interfaz y múltiples implementaciones. Un interfaz ya que el interfaz (la declaración del método) es idéntico. Múltiples implementaciones: hay múltiples formas de calcular el área.

23 Polimorfismo La sobreescritura de métodos permite el polimorfismo. La clase madre especifica un método, que las clases derivadas sobreescriben, de tal forma que cada clase derivada tiene su implementación del método Por ello, se habla de polimorfismo: un interfaz (la misma declaración de método) y múltiples implementaciones (múltiples formas de calcular el área) Veamos el siguiente ejemplo. Tenemos una referencia (f) de la clase madre Figura ¿cómo sabe el programa el método getArea() que debe ejecutar?, en función del objeto que se crea (en este caso un círculo): public static void main(String[] args) { Figura f; f = new Circulo( 40, 40, 3 ); System.out.println( f.getArea() ); f = new Rectangulo( 12, 10, new Punto(30, 33) ); }

24 Las clases hijas deben implementar el método abstracto
Clases abstractas En nuestro ejemplo de figuras la clase madre no tiene implementación del método getArea() Esto no es raro en una clase madre, en muchas ocasiones las clases más elevadas en una jerarquía de herencia especifican un interfaz (declaración de función), sin definir una implementación. En nuestro ejemplo no se puede calcular el área de una figura (si de un Rectángulo o un Círculo) Denominamos clases abstractas a aquellas que tienen o heredan un método abstracto, es decir, métodos declarados pero no implementados. No puede haber instancias de las clases abstractas, lo que no resulta extraño: nos puede interesar que se hagan instancias de Rectangulo o Circulo, pero no permitir instancias de Figura. En nuestro ejemplo la clase madre puede ser: package figuras.dominio; abstract public class Figura { protected Punto posicion; public void setPosicion(Punto posicion) { this.posicion = posicion; } public Punto getPosicion() { return posicion; abstract public double getArea(); Las clases hijas deben implementar el método abstracto

25 final La palabra final tiene varios sentidos:
Precediendo a una variable: la define como una constante y no puede ser modificada Precediendo a un método: indica que el método no puede ser sobrescrito Precediendo a una clase: indica que no se pueden definir subclases

26 Agregación Una relación en la que el objeto es una parte de otro objeto (como la batería es una parte del teléfono móvil). Nos ayudaremos de la clase Vector del JDK

27 Agregación: composición
En ocasiones interesa crear objetos que se componen de otros objetos La parte desaparece cuando desaparece el todo public class Casa { private Dormitorio dormitorios[]; private Salon salon; private Cocina cocina; }

28 Agregación: contenedor
El componente (por ejemplo, un producto de un catálogo comercial) no desaparece con el contenedor (catálogo) Conviene usar alguna de las clases del JDK para hacer colecciones de objetos, por ejemplo Vector, ArrayList, etc. Están en el paquete java.util

29 Vector (I) En Java las matrices son de longitud fija, una vez creadas no se puede modificar su tamaño Hay un truco para saltarse esta limitación: crear otra matriz más grande y copiar de la matriz original a la nueva Java nos suministra una clase, denominada Vector, que nos permite utilizar una matriz de longitud variable Constructores: Vector(): tamaño inicial de 100 y se incrementa duplicando tamaño Vector( int tamaño ): señala el tamaño y se incrementa duplicando tamaño Vector( int tamaño, int incremento ): señala tamaño e incremento Para añadir: add( Object elemento ). Ejemplo: Vector v = new Vector(50,5); v.add( new Persona( “Pedro” ) ); En muchos métodos se tiene en cuenta el índice o posición dentro del Vector. Por ello, conviene recordar que el primer elemento es el cero. Para insertar en una posición determinada (desplazando el resto hacia la derecha): insertElementAt( Object elemento, int posición )

30 Vector (II) Para conocer el número de elementos, así como la capacidad del Vector tenemos int size() e int capacity(): Vector v = new Vector(50,5); v.addElement( new Persona( “Pedro” ) ); System.out.println( v.size() ); // Muestra 1 System.out.println( v.capacity() ); // Muestra 50 Para eliminar un elemento: removeElementAt( int posición ). Desplaza los elementos de la derecha para llenar el hueco. Para obtener el elemento de la posición indicada: Object elementAt( int posición ) u Object get( int posición )

31 Versión “final” del proyecto de las figuras
Vamos a ver como queda finalmente el proyecto de las figuras, en el que se ha aplicado: Modularización: se ha separado en tres paquetes la presentación, clases del dominio y la clase de inicio Encapsulamiento: datos privados. Tambien la complejidad de la presentación queda encapsulada (oculta), al final no hay más que llamar al método “VistaFiguras.mostrar()”, pasando como argumento el objeto que se quiere mostrar Sobrecarga de métodos Herencia: hay una clase madre “Figura” que tiene métodos y atributo para la posición de la figura Clase abstracta: la clase “Figura” es además abstracta, ya que tiene el método abstracto “getArea()” Agregación: la clase Pagina es un contenedor de figuras Una clase (Figura) usa de otra (Punto). El atributo “Punto posicion” de la clase Figura hace referencia al punto de su posición Atributo static y final: “Circulo.PI”, ya que sólo hay un número PI, aunque haya cero o millones de círculos Métodos static: “VistaFigura.mostrar()”, no hace falta instanciar la clase para llamar al método. Es una forma de implementación común en clases de utilidad.

32 Versión “final” del proyecto de las figuras. Los puntos y las figuras
package figuras.dominio; public class Punto { private int x; private int y; public Punto(int x, int y) { setPunto(x, y); } public Punto(Punto p) { setPunto(p ); public void setPunto(int x, int y) { this.x = x; this.y = y; public void setPunto(Punto p) { x = p.getX(); y = p.getY(); public int getX() { return x; } public int getY() { return y; } public String toString() { return "(" + x + "," + y + ")"; Todas las figuras usan de Punto, ya que todas heredan el atributo 'posicion' de Figura Figura es una clase abstracta package figuras.dominio; abstract public class Figura { protected Punto posicion; public void setPosicion(Punto posicion) { this.posicion = posicion; } public Punto getPosicion() { return posicion; abstract public double getArea();

33 Versión “final” del proyecto de las figuras. Los círculos
package figuras.dominio; public class Circulo extends Figura { private double radio; static final public double PI = ; public Circulo() { } public Circulo( double nuevoRadio, Punto nuevaPosicion ) { setRadio( nuevoRadio ); setPosicion( nuevaPosicion ); } public Circulo( double nuevoRadio, int posicionX, int posicionY ) { posicion = new Punto( posicionX, posicionY ); public Circulo( Circulo circulo ) { setRadio( circulo.getRadio() ); setPosicion( circulo.getPosicion()); public void setRadio( double radio ) { this.radio = radio; } public double getRadio() { return radio; } public double getArea() { return radio * radio * PI; } public String toString() { return "Radio: " + radio + " Posicion: " + posicion.toString() + " Area: " + getArea(); Los círculos heredan de Figura, por tanto tienen que implementar el método getArea()

34 Versión “final” del proyecto de las figuras. Los rectángulos
package figuras.dominio; public class Rectangulo extends Figura { private double ancho; private double largo; public Rectangulo( double ancho, double largo, Punto posicion ) { setDimensiones( ancho, largo ); setPosicion(posicion); } public void setDimensiones( double ancho, double largo ) { this.ancho = ancho; this.largo = largo; public double getArea() { return ancho * largo; public String toString() { return "Ancho: " + ancho + " Largo: " + largo + " Posicion: " + posicion.toString() " Superficie: " + getArea();

35 Versión “final” del proyecto de las figuras. La página (agregador)
package figuras.dominio; import java.util.*; public class Pagina { private Vector vecFiguras = new Vector(); public void agregar( Figura fig ) { vecFiguras.add( fig ); } public boolean desagregar( int indice ) { if ( indice < vecFiguras.size() ) { try { vecFiguras.remove(indice); return true; catch (Exception e) { return false; } return false; public Figura obtener( int indice ) { return (Figura) vecFiguras.get(indice); catch (Exception e) { return null; } return null; public int tamanio() { return vecFiguras.size(); } Utilizamos la clase java.util.Vector para agregar figuras La utilidad de usar la herencia (o los interface lógicos): en “agregar()” usamos una referencia genérica (Figura fig). Este método puede recibir cualquier clase hija (Rectangulo, Circulo, etc.). Es muy interesante ya que, si no fuese así, tendríamos que implementar el método agregar para cada una de las subclases En “obtener()” no eliminamos o quitamos el objeto del vector, simplemente devolvemos una referencia al objeto. ¿Por qué necesitamos hacer casting? Gestionamos excepciones con try - catch

36 Versión “final” del proyecto de las figuras. La visualización
package figuras.presentacion; import figuras.dominio.*; public class VistaFiguras { public static void mostrar( Circulo cir ) { if ( cir != null ) System.out.println( cir.toString() ); else System.out.println( "Error al intentar mostrar el círculo" ); } public static void mostrar( Rectangulo rec ) { if ( rec != null ) System.out.println( rec.toString() ); System.out.println( "Error al intentar mostrar el rectángulo" ); public static void mostrar( Pagina pag ) { if ( pag == null ) { System.out.println("Error al intentar mostrar la página"); return; for ( int i = 0; i < pag.tamanio(); i++ ) { Figura fig = pag.obtener(i); System.out.println( fig.toString() ); Aspectos a resaltar: Polimorfismo con sobrecarga de métodos: todos los métodos se llaman igual, pero actúan de forma diferente (patrón estrategia) Para mostrar las figuras de una página: recorremos los elementos con un for(). Dentro del for(), para obtener la figura (sea un rectángulo o un círculo) usamos una referencia genérica del tipo “Figura” (clase madre), a la que mandamos el mensaje toString(). Un ejemplo típico de polimorfismo: usamos el mismo mensaje (toString) para producir comportamiento diferente, en función del objeto unos se representan de una forma y otros de otra

37 Versión “final” del proyecto de las figuras. El inicio
import figuras.presentacion.*; class Inicio { //// Usa agregador public static void main(String[] args) { Circulo primero = new Circulo( 23, 2, 3 ); Circulo copia = new Circulo( primero ); Circulo tercero = new Circulo( 17, new Punto(8,9) ); Rectangulo rectangulo = new Rectangulo(3.5, 2, new Punto(55,54)); Pagina pag = new Pagina(); pag.agregar( primero); pag.agregar( copia); pag.agregar( tercero ); pag.agregar( rectangulo ); VistaFiguras.mostrar( pag ); } Puesto que el método mostrar() es static podemos usar VistaFiguras sin instanciarla


Descargar ppt "Introducción a Java (II)"

Presentaciones similares


Anuncios Google