Title: Introduccin a Java II
1Introducción a Java (II)
2Índice
- Introspección
- Introducción a las relaciones
- Herencia
- Agregación
3Introspección
4Introducció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
5Conocer 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
6newInstance()
- 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()
)
7Introducción a las relaciones
8Introducció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)
9Introducció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)
10Introducció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
11Herencia
- 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
12Un 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
13Algunas 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
14Mejorando 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 ) super(
marcaModelo, precioDia ) System.out.println(
"Ha creado un autobus" ) void
mostrarCaracteristicas() super.mostrarCaract
eristicas() System.out.println( " Plazas "
plazas )
DESDE MAIN() Autobus v1 new Autobus( "Volvo
550", 120, 57) v1.mostrarCaracteristicas() v1.de
fPrecioDia( 130 ) v1.mostrarCaracteristicas()
Construyo un vehículo Ha creado un autobus Marca
Volvo 550 Precio 120 Plazas 57 Marca Volvo 550
Precio 130 Plazas 57
15Notas al ejemplo
- En Vehiculo
- 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
16Los 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 )
-
17Modificadores de acceso y herencia
18Herencia 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
19Herencia 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.
20Herencia 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
21Herencia 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.
22Otro 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
3.1416 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.
23Polimorfismo
- 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)
) - System.out.println( f.getArea() )
-
24Clases 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
25final
- 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
26Agregació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
27Agregació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
28Agregació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
29Vector (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 )
30Vector (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 )
31Versió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.
32Versió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()
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()
33Versió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 3.1416
- public Circulo()
- public Circulo( double nuevoRadio, Punto
nuevaPosicion ) - setRadio( nuevoRadio )
- setPosicion( nuevaPosicion )
-
- public Circulo( double nuevoRadio, int
posicionX, int posicionY ) - setRadio( nuevoRadio )
- posicion new Punto( posicionX,
posicionY ) -
- public Circulo( Circulo circulo )
- setRadio( circulo.getRadio() )
- setPosicion( circulo.getPosicion())
-
Los círculos heredan de Figura, por tanto tienen
que implementar el método getArea()
34Versió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()
35Versió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 lt vecFiguras.size() )
- try
- vecFiguras.remove(indice)
- return true
-
- catch (Exception e) return false
-
- return false
-
- 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
36Versió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() )
- else
- System.out.println( "Error al
intentar mostrar el rectángulo" ) -
- 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
37Versió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