Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Tema 3
Tema 3
Objetos
Diseño Software (614G01015)
Índice
1 Abstracción y Encapsulamiento
2 Modularidad
3 Jerarquı́a
4 Polimorfismo
5 Tipificación
6 Ligadura Dinámica
Índice
1 Abstracción y Encapsulamiento
Abstracción
Encapsulamiento
2 Modularidad
3 Jerarquı́a
4 Polimorfismo
5 Tipificación
6 Ligadura Dinámica
Abstracción
Abstracción
Representación de las caracterı́sticas fundamentales de algo sin incluir
antecedentes o detalles irrelevantes.
Abstracción
Encapsulamiento
Encapsulamiento
Proceso de ocultación al exterior de aquellos detalles de la
implementación de un objeto que no es necesario conocer para su uso.
Ventajas
Ocultación de la información
La supresión de los detalles de bajo nivel nos permite razonar acerca
de la operación u objeto de forma más eficiente.
Un cambio en la representación interna, si no afecta a la parte
pública de un objeto, no afectará a los clientes que usen dicho objeto
(ej. cambiar la implementación de una función por otra más
eficiente).
Representación conjunta de estado y comportamiento
Todo lo relacionado con un objeto determinado está situado en la
clase software que representa a ese objeto ⇒ naturalidad,
simplicidad, etc.
Índice
1 Abstracción y Encapsulamiento
2 Modularidad
Definición y Caracterı́sticas
Modularidad en Java
Paquetes Java
Objetivos de los Paquetes
3 Jerarquı́a
4 Polimorfismo
5 Tipificación
6 Ligadura Dinámica
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 9
Abstracción y Encapsulamiento
Modularidad Definición y Caracterı́sticas
Jerarquı́a Modularidad en Java
Polimorfismo Paquetes Java
Tipificación Objetivos de los Paquetes
Ligadura Dinámica
Modularidad
Modularidad
Propiedad que tiene un sistema que ha sido descompuesto en un
conjunto de partes o módulos que sean cohesivos (guardan cierta relación
lógica) y débilmente acoplados (minimizan sus dependencias).
Ventajas
Ventajas
El hecho de fragmentar un programa en componentes individuales
suele contribuir a reducir su complejidad.
Permite crear una serie de fronteras bien definidas y dentro del
programa ⇒ aumenta la comprensión del mismo.
Relación con el encapsulamiento
Los módulos pueden ocultar información a otros módulos actuando
como mecanismos de encapsulamiento a un nivel superior al
encapsulamiento de las propias clases.
Modularidad en Java
Modularidad en Java
Ficheros Java
Actúan como unidades fı́sicas de compilación.
Modularidad en Java
Paquetes Java
Son unidades lógicas de agrupación de clases.
Ocultación de información: Las clases que no son públicas solo son
visibles dentro del propio paquete.
Definición de paquetes
Importación de paquetes
Importación de paquetes
La sentencia import
Especifica que los contenidos públicos del paquete especificado entran en
el espacio de nombres del fichero de origen
package otropaquete;
import graficos.Circulo;
// ...
Importación de paquetes
Formas de importación:
Importar una clase concreta
Importar todas las clases públicas de un paquete
package otropaquete;
import graficos.Circulo; // Importa solo la clase Circulo
import graficos.*; // Importa todas las clases públicas del paquete
Importación de paquetes
Importante...
No es necesario incluir la sentencia import para dar privilegios de
acceso de un paquete a otro
Jerarquı́as de paquetes
Importante...
Aunque el nombre del paquete puede tener varios niveles no existe el
concepto de “subpaquete” ni relaciones jerárquicas entre los paquetes
Índice
1 Abstracción y Encapsulamiento
2 Modularidad
3 Jerarquı́a
Composición
Herencia
Clases Abstractas
Interfaces
Herencia vs. Composición
4 Polimorfismo
5 Tipificación
6 Ligadura Dinámica
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 27
Abstracción y Encapsulamiento Composición
Modularidad Herencia
Jerarquı́a Clases Abstractas
Polimorfismo Interfaces
Tipificación Herencia vs. Composición
Ligadura Dinámica
Jerarquı́a
Jerarquı́a
Una jerarquı́a es una clasificación de las abstracciones
Composición
Composición
Define relaciones TIENE UN y ocurre cuando un elemento contiene otros
elementos (los objetos incluyen otros objetos en la definición de su
estado).
Composición
Objeto simple
Composición
Objeto compuesto
Herencia
Herencia
Relación ES UN entre clases, en las que una clase hereda la estructura y
el comportamiento definidos en una o más clases.
Herencia
Funcionamiento
Una subclase hereda de una o más superclases y aumenta o redefine
la estructura y el comportamiento de dichas superclases.
Generalización vs. Especialización
Las subclases representan conceptos especializados.
Las superclases representan generalizaciones de los aspectos comunes
de las subclases.
Según como se lea la relación de herencia puede ser vista como una
generalización o una especialización.
En Java
La herencia se especifica usando la cláusula extends al principio de
la definición de una clase.
Ejemplo de herencia
nombreCompleto y
direccionFisica.
Métodos propios:
calcularMatricula.
Ejemplo de herencia
Estudiante Profesor
Persona Persona
Estructuras de herencia
Herencia simple
Cada clase tiene, como máximo, un ancestro.
Lenguajes:
Java, Object Pascal, C#, etc.
Ventajas:
Las estructuras de herencia son sencillas y tienen forma de árbol.
Estructuras de herencia
Clase raı́z
Clase que actúa como superclase del
resto de clases. Object
Estructuras de herencia
Estudiante Profesor
Persona Persona
Object Object
Estructuras de herencia
Herencia múltiple
Una clase puede heredar de varias clases simultáneamente.
Lenguajes:
C++, Eiffel, etc.
Ventajas:
Una clase puede heredar caracterı́sticas de varias superclases no
relacionadas ⇒ más posibilidades de diseño.
Inconvenientes:
Genera conflictos cuya resolución es especı́fica de cada lenguaje ⇒
aumenta la complejidad de la programación y reduce la
comprensibilidad.
La tendencia actual es evitar la herencia múltiple ya que es posible
desarrollar jerarquı́as de herencia complejas y flexibles con herencia
simple ⇒ ver Interfaces.
Herencia y constructores
¿Qué constructor hay en SubClase?
Un objeto de una subclase tiene
class SuperClase { siempre un objeto de una
private int valor;
superclase dentro.
public SuperClase(int valor) { Antes de crear el objeto de la
this.valor = valor;
} subclase se crea primero el
} objeto de la superclase
class SubClase extends SuperClase { La palabra clave super se usa
private int subvalor;
// Constructor ? para llamar a los constructores
de la superclase (como this se
usaba para llamar a otro
constructor de la misma clase).
No se puede usar super() y
this() en un mismo
constructor.
Clases abstractas
Clase abstracta
Clase que no pueden ser instanciada (no se puede crear un objeto de
la misma) y que está destinada a ser extendida por herencia.
En Java se crea anteponiendo abstract a class.
Ejemplo
1 Definir clases generales que public abstract class Animal {
agrupan caracterı́sticas comunes private String nombre;
private String codigo;
Son clases destinadas a la private Date fechaNacimiento;
herencia y de las cuales no tiene
sentido que existan instancias. public String getNombre()
{ return nombre; }
P. ej. una tienda de animales }
agrupa las caracterı́sticas
comunes de todos en la clase public class Gato extends Animal {
Animal. public enum variedadGato
{SIAMES, ESFINGE};
No tiene sentido que existan }
instancias de Animal pero si de
las subclases concretas (Gato, public class Perro extends Animal {
public enum variedadPerro
Perro, etc.). {PASTOR_ALEMAN, LABRADOR};
}
Métodos abstractos
Método abstracto
Método en el que sólo se define su interfaz (nombre, parámetros y tipo
de retorno) pero no su implementación (que se difiere a las subclases).
Ejemplo
2 Diferir comportamiento a las
subclases public abstract class Figura {
Hay métodos cuyo interfaz public abstract double area();
public abstract double perimetro();
podemos definir en la superclase }
pero cuya implementación es
propia de cada una de las public class Circulo extends Figura {
private double radio;
subclases. public double area() {
P. ej. podemos definir un método return Math.PI*radio*radio;
}
abstracto area() en Figura
pero la implementación del public double perimetro() {
método dependerá de cada figura return 2*Math.PI*radio;
}
en concreto (Circulo, // ...
Rectangulo, etc.). }
3 Asegurar la consistencia en el
Ejemplo
interfaz de todas las subclases
Las subclases están obligadas a public abstract class Figura {
implementar los métodos public abstract double area();
public abstract double perimetro();
heredados de una clase abstracta, }
eso permite asegurar que todas
las subclases tienen una class Rectangulo extends Figura {
private double base;
implementación de dicho método private double altura;
con el interfaz definido en la public double area() {
superclase. return base*altura;
}
P.ej. Al indicar que la clase
Rectangulo hereda de Figura public double perimetro() {
return (base*2)+(altura*2);
el compilador obliga a la primera }
a dar una implementación a los // ...
métodos area() y }
perimetro().
Clases abstractas
Las clases abstractas pueden incluir elementos no abstractos
Interfaces
Interfaces
Son clases abstractas “puras” que definen un protocolo de conducta pero
no cómo debe implementarse dicha conducta.
Interfaces en Java
Interfaces en Java
Declaración de un interfaz
[public] interface Nombre [extends superinterfaz, ...]
{ // Constantes de clase // Métodos abstractos }
Ejemplo de interfaz
Ejemplo
Monstruo
interface Monstruo {
void amenaza();
}
interface MonstruoPeligroso extends Monstruo {
void destruye();
Monstruo
} Letal
Peligroso
interface Letal {
void mata();
}
interface Vampiro extends MonstruoPeligroso,
Letal {
void bebeSangre(); Vampiro
}
Implementación de interfaces
Implementación de interfaces
Implementar un interfaz consiste en darle una implementación concreta a
todos y cada uno de los métodos que contiene.
Ejemplo
Implementación de interfaces
Las clases pueden implementar múltiples interfaces.
Monstruo
Ejemplo
Virus
Implementación de interfaces
Una clase puede evitar dar una implementación a los métodos del
interfaz declarándose abstracta.
Ejemplo
Implementación de interfaces
Ejemplo
Object Monstruo
Organismo Monstruo
Vivo Letal
Peligroso
Serpiente
Dracula Herencia entre interfaces
Cascabel
Herencia múltiple sin clase raíz
Interfaz Ordenacion
public interface Ordenacion {
void ordena(int[] array);
}
Clase Hero
public class Hero implements
Clase Monster
CanFight, CanSwim, CanFly {
public class Monster implements
public void fight() { CanFight, CanFly {
System.out.println("A hero can fight");
} public void fight() {
System.out.println("Monster fight");
public void swim() { }
System.out.println("A hero can swim");
} public void fly() {
System.out.println("Monster fly");
public void fly() { }
System.out.println("A hero can fly"); }
}
}
Clase Scanner
public class Scanner {
public Scanner(String source) { ... };
public Scanner(File source) throws FileNotFoundException { ... }
public Scanner(InputStream source) { ... }
public Scanner(Readable source) { ... }
// ...
}
Interfaz Readable
public interface Readable {
public int read(CharBuffer cb) throws IOException;
}
Ejemplo
Anotaciones
Son metadatos, es decir, información sobre los elementos del programa.
Son similares a las etiquetas del javadoc (como @author) pero
aplicadas al código, no a los comentarios.
La anotación @Deprecated
Declara un método como depreciado para que el compilador avise de
que no debe usarse.
Es diferente a la misma anotación usada en Javadoc y que,
sorprendentemente, el compilador también leı́a (y lo sigue haciendo
por motivos de compatibilidad hacia atrás).
Actualmente se recomienda usar las dos anotaciones, una para los
comentarios y otra para el compilador.
La anotación @Deprecated
public class ClaseDeprecated {
/**
* Hace algo...
* @param valor sobre el que se hace algo
* @deprecated Usar mejor el método hacerAlgo
*/
@Deprecated public void hazAlgo(int valor) { }
// ...
}
Anotaciones personalizadas
Creamos una anotación que registrará
Ejemplo el porcentaje de desarrollo de una cla-
se. Al marcarla como runtime pue-
de ser leida en tiempo de ejecución
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface EnDesarrollo { int porcentaje(); }
@EnDesarrollo (porcentaje=50)
class MiClase { }
Creamos una clase y marcamos su porcentaje
public class AnotacionSimple {
public static void main(String[] args) {
MiClase x = new MiClase();
Class c = x.getClass();
if (c.isAnnotationPresent(EnDesarrollo.class)) {
System.out.println("Esta clase esta en desarrollo");
EnDesarrollo ed =
(EnDesarrollo)c.getAnnotation(EnDesarrollo.class);
System.out.println("Porcentaje = " + ed.porcentaje());
}
}
} Podemos leer ese porcentaje en tiempo de ejecución
Ejemplo
Problemas:
Los interfaces que implementa una clase es algo público y notorio,
estamos mostrando en dicho interfaz un aspecto de la
implementación (de donde obtenemos la constante PI) ⇒ Polución
del API exportado.
Estamos creando una dependencia entre Circulo y Constantes
que al ser pública puede no ser fácil de cambiar.
Soluciones:
Crear las constantes como tipos enumerados.
Crear las constante en clases relacionadas (MIN VALUE y
MAX VALUE están en la clase Integer).
Crear clases de utilidad para alojar dichas constantes (la clase Math
aloja constantes matemáticas como PI o E).
// ...
}
class Circulo {
private double radio;
class Circulo {
private double radio;
Dos notaciones alternati-
vas: importar un elemento
public double area() { concreto o importar to-
return PI*radio*radio; dos los elementos estáticos
}
public double perimetro() {
return 2*PI*radio;
} Podemos usar los elemen-
// ... tos estáticos sin antepo-
} ner el nombre de la clase
Interfaz
Collection
Clase abstracta
Clase concreta
Abstract
Set Queue List
Collection
Enumerados e interfaces
interface CanMakeNoise {
String makeNoise();
}
Enumerados y colecciones
Ejemplo de EnumSet
Enumerados y colecciones
Y también una implementación de Map especı́ficamente dedicada a
trabajar con enumerados: EnumMap
Ejemplo de EnumMap
enum DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
class Main {
public static void main(String[] args) {
EnumMap<DayOfWeek, String> activityMap
= new EnumMap<>(DayOfWeek.class);
activityMap.put(DayOfWeek.MONDAY, "Soccer");
activityMap.put(DayOfWeek.TUESDAY, "Basketball");
// ...
System.out.println(activityMap.get(DayOfWeek.MONDAY)); // Soccer
}
}
Ejemplo
//...
}
Ventajas de la herencia
Ventajas de la composición
La composición indica con claridad cuales son las operaciones
que podemos utilizar en la nueva estructura de datos definida
La herencia es menos clara de comprender porque obliga a analizar la
estructura de herencia para comprender mejor el funcionamiento de
la clase creada (“problema del yo-yo”).
La composición permite no “heredar” métodos indeseados (como
removeElementAt en Stack) .
La composición es más fácil de modificar
Si el objeto interno es privado es fácil cambiarlo por otro afectando
sólo a la clase local y no a otras clases que lo usen.
La composición no implica sustitución
La composición es una relación TIENE UN y no ES UN. Por lo que
evita el principio de sustitución propio de la herencia.
La composición es una solución más general
La herencia tiene limitaciones (herencia simple vs. múltiple) que no
tiene la composición (podemos crear una composición de varias
clases y usar sólo aquellos métodos que nos convengan).
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 90
Abstracción y Encapsulamiento Composición
Modularidad Herencia
Jerarquı́a Clases Abstractas
Polimorfismo Interfaces
Tipificación Herencia vs. Composición
Ligadura Dinámica
Conclusiones
Usar herencia exclusivamente en aquellas relaciones que claramente
corresponden a un tipo ES UN y en las que tiene sentido pasar un
elemento de la subclase como un elemento de la superclase.
Usar composición en aquellas relaciones que no se correspondan a
un tipo ES UN claro.
En caso de duda favorecer siempre la composición sobre la herencia.
Índice
1 Abstracción y Encapsulamiento
2 Modularidad
3 Jerarquı́a
4 Polimorfismo
Tipos de Polimorfismo
Sobrecarga
Coacción
Polimorfismo de Inclusión
Polimorfismo Paramétrico
5 Tipificación
6 Ligadura Dinámica
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 92
Abstracción y Encapsulamiento Tipos de Polimorfismo
Modularidad Sobrecarga
Jerarquı́a Coacción
Polimorfismo Polimorfismo de Inclusión
Tipificación Polimorfismo Paramétrico
Ligadura Dinámica
Polimorfismo
Polimorfismo
Capacidad de un objeto de pertenecer a más de una clase y de una
función de ser aplicada sobre parámetros de distintas clases.
Paramétrico
Polimorfismo
Sobrecarga
Sobrecarga
Sobrecarga (overloading)
Consiste en utilizar el mismo nombre para denotar métodos distintos
diferenciándolos por el número o tipo de los parámetros.
Sobrecarga
// Constructores
public Caja() { this(0, 10); }
Sobrecarga
El polimorfismo es sólo aparen-
Sobrecarga en otros métodos te, no existe una función que
acepte distintos parámetros,
public class Sobrecarga { sino distintas funciones que
public void metodoX (String s) { comparten el mismo nombre
System.out.println("Cadena " + s );
}
Sobrescritura
Sobrescritura (overriding)
Situación que ocurre cuando una clase hija le da un implementación mas
especı́fica a un método implementado y heredado de una clase padre.
Sobrescritura y sobrecarga
Sobrescritura
class Animal {
public void display() {
System.out.println("I am an animal");
}
}
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.display(); // I am a dog
}
}
Sobrescritura de refinamiento
Sobrescritura de refinamiento: Los métodos de la superclase son
accedidos desde la subclase usando la palabra clave super
super serı́a un puntero a la superclase (de la misma forma que
this es un puntero a la clase actual).
Variaciones en la sobrescritura
class Animal {
protected void display() { // protected
System.out.println("I am an animal");
}
}
Variaciones en la sobrescritura
Anotación @Override
¿Es esta sobrescritura de equals correcta?
Anotación @Override
Anotación @Override
@Override
public boolean equals(Caja caja) {
if (caja == null) { return false; }
if (this.valor != caja.valor) { return false; }
return true;
}
}
Coacción
Coacción (o Coerción)
Operación semántica por la cual se convierte un argumento al tipo
esperado por una función para evitar que se produzca un error de tipos.
Coacción
Coacción
Coacción
Reglas de ensanchamiento (widening) y estrechamiento
(narrowing) de tipos primitivos en Java.2
Polimorfismo de inclusión
Polimorfismo de inclusión (o polimorfismo de subtipos)
Polimorfismo que ocurre a través de las relaciones de herencia y mediante
el cual una instancia de una subclase es también un instancia de sus
superclases. Es el polimorfismo tı́pico de la POO.
Polimorfismo de inclusión
El polimorfismo de inclusión permite que un objeto de una subclase
puede utilizarse en aquellos lugares en los que se requiere un objeto
de alguna de sus superclases (una asignación de variables, un paso
de parámetros, etc.).
¡Ojo! Lo contrario no es cierto, no podemos asignar un objeto de
una superclase a una variable de las subclases.
Polimorfismo de inclusión
¿Cuál es el resultado de la ejecución de este código?
class Polimorfismo {
public static void main(String[] args) {
Perro p = new Perro(); // Perro hereda de Animal
if (p instanceof Perro) System.out.println("p es Perro");
if (p instanceof Animal) System.out.println("p es Animal");
Métodos genéricos
Colecciones genéricas
Colección genérica de objetos: ArrayList (versión simplificada)
Colecciones genéricas
// ...
ArrayList perros = new ArrayList();
perros.add(new Perro("Milu"));
perros.add(new Perro("Snoopy"));
Colecciones genéricas
Colecciones genéricas
1 No podemos hacer colecciones de tipos primitivos
Por motivos de eficiencia los tipos primitivos de Java (int, float,
etc.) no son objetos.
Las colecciones basadas en el polimorfismo de inclusión pueden
almacenar cualquier subclase de Object, pero no pueden almacenar
tipos primitivos.
Clases envoltorio
Clases envoltorio
Clase envoltorio Integer (version simplificada)
// Constantes de utilidad
public static final int MIN_VALUE = 0x80000000;
public static final int MAX_VALUE = 0x7fffffff;
// Métodos de utilidad
// Valor convertido a double
public double doubleValue() { return (double)value; }
// Valor convertido a String
public String toString() { return toString(value); }
public static String toString(int i) {
// Método estatico para convertir cualquier int a String
}
//...
}
Clases envoltorio
Boxing
Proceso de introducir un tipo primitivo en su correspondiente clase
envoltorio.
Unboxing
Proceso de extraer un tipo primitivo de su correspondiente clase
envoltorio.
Clases envoltorio
Problema de las clases envoltorio: su utilización se hace muy
engorrosa.
Ejemplo
Convertimos el ob-
// ArrayList es una coleccion jeto a Integer pa-
// implementada como un array de objetos
ArrayList l = new ArrayList();
ra insertarlo en la lista
l.add(new Integer(1));
l.add(new Integer(2)); Lo sacamos de la lista y lo
l.add(new Integer(3)); volvemos a convertir en int
Integer i = (Integer)l.get(0);
System.out.printf("El valor de 0 es %d", i.intValue());
// ...
Solución: autoboxing/autounboxing
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 126
Abstracción y Encapsulamiento Tipos de Polimorfismo
Modularidad Sobrecarga
Jerarquı́a Coacción
Polimorfismo Polimorfismo de Inclusión
Tipificación Polimorfismo Paramétrico
Ligadura Dinámica
Clases envoltorio
Importante: Desde Java 9 el constructor de Integer esta deprecated.
Se recomienda usar el método estático Integer valueOf(int).
Al almacenar en caché valores enteros solicitados frecuentemente
(como mı́nimo los del rango -128 a 127), tiene un rendimiento de
tiempo y espacio significativamente mejor.
Ejemplo
Clases envoltorio
Autoboxing
Proceso mediante el cual un tipo primitivo es convertido
automáticamente en su clase envoltorio cuando se requiere que se
comporte como un objeto.
Autounboxing
Proceso mediante el cual un objeto de una clase envoltorio es convertido
automáticamente en su tipo primitivo cuando se requiere que se
comporte como dicho tipo primitivo.
Clases envoltorio
Ejemplo de Autoboxing/Autounboxing
Autoboxing: un int
// ... se convierte automáti-
ArrayList l = new ArrayList();
camente en Integer
l.add(1);
l.add(2);
l.add(3); Autounboxing: Un inte-
ger se convierte en un int
Integer i = (Integer)l.get(0);
System.out.printf("El valor de 0 es %d", i);
// ...
Colecciones genéricas
Pérdida de identidad
Ejemplo l:ArrayList
// Error de compilación
Perro p2 = l.get(0); l.add(p1)
// error: incompatible types
// required: Perro found: Object p3:Perro
// Error ejecución
Gato g1 = (Gato)l.get(0); Object
// Perro cannot be cast to Gato Perro p3 =
(Perro)l.get(0);
Colecciones genéricas
3 No podemos hacer colecciones de tipos seguras
Si las colecciones son de tipo Object quiere decir que cualquier
objeto puede ir en las mismas.
No hay nada que evite que en una colección de perros introduzcamos
un gato.
El problema viene al extraer los elementos de la colección y hacer
una conversión explı́cita a Perro.
Solución para estos dos últimos problemas ⇒ Genericidad.
Ejemplo
Polimorfismo paramétrico
Polimorfismo paramétrico (genericidad)
Consiste en que una clase tiene uno, o varios, parámetros genéricos
definidos. En la instanciación de dicha clase se especificará qué valores
concretos tienen dichos parámetros genéricos.
Polimorfismo paramétrico
Genericidad
Clases genéricas
Parámetros de tipo
Instanciando clases genéricas
Colecciones y genericidad
Colecciones genéricas
Ventajas
Bucle for-each
Tipos vinculados
Comodines
Genericidad y subclases
Covarianza
Parámetros y subclases
Comodines y Principio Get y Put
Métodos genéricos
Interfaces y genericidad
Complicaciones de la genericidad
Compatibilidad hacia atrás (Erasure)
El warning unchecked
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 134
Abstracción y Encapsulamiento Tipos de Polimorfismo
Modularidad Sobrecarga
Jerarquı́a Coacción
Polimorfismo Polimorfismo de Inclusión
Tipificación Polimorfismo Paramétrico
Ligadura Dinámica
Parámetros de tipo
Son parámetros genéricos que especifican un tipo indeterminado.
Pueden representar a cualquier tipo que no sea un tipo primitivo:
una clase, un interfaz, un array, etc.
Por convención se representan con una única letra mayúscula,
usando habitualmente T (Tipo), E (Elemento), K (Clave) o V
(Valor) o N (Número).
La clase T no se define nunca, será sustituida por el parámetro
pasado al hacer el new de la clase que contiene el parámetro T.
Colecciones y genericidad
Clase ArrayList con genericidad (versión simplificada)
Colecciones y genericidad
Ahora cuando se crea la colección se especifica su tipo mediante el
parámetro genérico.
La colección se considera ahora de tipos seguros. Si intentamos
introducir un objeto de otro tipo en la colección sucederá un error de
compilación (p.ej. si introducimos un gato en una colección de
perros).
Colecciones y genericidad
Seguridad
Se realizan comprobaciones en tiempo de compilación sobre el tipo
de la colección.
Se evitan errores en tiempo de ejecución.
Claridad
Se evitan molestas conversiones de tipos al extraer un elemento de la
colección.
Comprensibilidad
Al definir la colección se especifica su tipo, por lo que resulta más
comprensible el motivo por el cuál se está creando la colección.
for(String s : arrayString)
System.out.println(s);
for(String s : listaString)
System.out.println(s);
Tipos vinculados
Del parámetro T realmente solo sabemos que es un Object.
Por lo que solo podemos llamar sobre él a los métodos declarados en
Object: toString, equals, hashCode, getClass, etc.
Tipos vinculados
Tipos vinculados (Bounded types)
Parámetros de tipo vinculados a una clase que hace de lı́mite superior. El
tipo genérico solo podrá ser instancia de cualquier subclase de la clase
lı́mite, o una instancia de la propia clase lı́mite.
Tipos vinculados
Tipos vinculados
Ejemplo de tipos vinculados
Comodines
A nuestra caja numérica le queremos añadir un método absEqual
que compara el valor absoluto de la caja actual con el valor absoluto
que hay dentro de la caja pasada por parámetro.
Nos encontramos con que no podemos comparar cajas de Integer
con cajas de Double.
Caja numérica con tipos vinculados
Comodines
Comodines (wildcards)
Un comodı́n es un tipo especial de parámetro que representa a un tipo
desconocido en la declaración de tipos genéricos.
Tipos de comodines
Comodines
Tipos vinculados
Un tipo vinculado puede tener múltiples vı́nculos con otras clases.
Solo podemos definir vı́nculos superiores.
Comodines
Un comodı́n solo puede tener un vı́nculo con otra clase.
Podemos definir vı́nculos superiores o inferiores.
Recomendaciones
Usar comodines en los parámetros de una función → ver Principio
Get y Put más adelante.
No usarlos como tipo de retorno de una función (para no forzar a los
clientes de nuestro código a usar comodines).
Usar los tipos vinculados en los parámetros genéricos de clases y
métodos (estos últimos los veremos más adelante).
Genericidad y subclases
Covarianza
Se denomina ası́ a la propiedad de los subtipos de seguir actuando como
tales en tipos más complejos (como las listas, los arrays, etc.).
printList(strList);
printList(intList);
Es correcta ya que hace lo que nos han pedido pero limitada. Solo
acepta listas de Figura y no listas de alguna subclase (Circulo o
Rectangulo).
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 167
Abstracción y Encapsulamiento Tipos de Polimorfismo
Modularidad Sobrecarga
Jerarquı́a Coacción
Polimorfismo Polimorfismo de Inclusión
Tipificación Polimorfismo Paramétrico
Ligadura Dinámica
Comodı́n extends
public static double sumaAreas(List<? extends Figura> l) {
double suma = 0;
for (Figura f : l)
suma += f.area(); // => Ver Ligadura Dinámica
return suma;
}
//...
List<Figura> figList = Arrays.asList(c1, c2, r1);
List<Circulo> cirList = Arrays.asList(c1, c2);
Conclusión
El comodı́n <?> y el comodı́n <? extends MiClase> tratan a las
colecciones como si fueran de sólo lectura.
Si nos piden hacer un método que dada una lista de figuras o una
lista de cualquiera de sus superclases podamos incluir un
Rectangulo en ellas.
Métodos genéricos
Imaginemos que nos piden una función estática que copie los
contenidos de una lista en otra.
La versión sin genericidad funciona sin problemas porque las dos son
listas de Object.
Métodos genéricos
Métodos genéricos
Métodos genéricos
Métodos que, de forma análoga a las clases, están parametrizados por un
parámetro de tipo situado antes del tipo de retorno.
Métodos genéricos
public static <T> void copy(List<? super T> dest, List<? extends T> src)
Métodos genéricos
Interfaces y genericidad
Interfaces y genericidad
Interfaces y genericidad
Interfaces y genericidad
Interfaces y genericidad
Hay que tener cuidado con las herramientas de autocompletado de
interfaces de los IDEs.
Si implementamos un interfaz sin genericidad...
...el IDE entenderá que queremos usar la versión sin genericidad del
mismo.
Interfaces y genericidad
...el IDE entenderá que queremos usar la versión con genericidad del
mismo.
Ejemplo
Proceso de Erasure
System.out.println("runtime type of ArrayList<String>: " +
new ArrayList<String>().getClass());
System.out.println("runtime type of ArrayList<Long> : " +
new ArrayList<Long>().getClass());
Ejemplos
Índice
1 Abstracción y Encapsulamiento
2 Modularidad
3 Jerarquı́a
4 Polimorfismo
5 Tipificación
Modos de Tipificación
Restricciones del Tipado en Java
Tipado del Pato (Duck Typing)
6 Ligadura Dinámica
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 193
Abstracción y Encapsulamiento
Modularidad Modos de Tipificación
Jerarquı́a Restricciones del Tipado en Java
Polimorfismo Tipado del Pato (Duck Typing)
Tipificación
Ligadura Dinámica
Tipificación
Tipos
Un tipo es una caracterización precisa de las propiedades estructurales y
de comportamiento que comparten una serie de entidades.
Tipificación
Tipado estático
La comprobación de tipos
se hace en tiempo de
compilación.
Tipado dinámico
La comprobación de tipos
se hace en tiempo de
ejecución.
Java Python
String s = "Hello, World"; s = "Hello, World"
Tipado fuerte
Las reglas de tipos son estrictas.
Tipado débil
Las reglas de tipos son más flexibles.
Tipado débil
Tipado fuerte
Ventajas: Hay menos
Ventajas: Se detectan más
restricciones a la hora de escribir
fácilmente los errores.
el código.
Inconvenientes: Ciertas
Inconvenientes: Código más
operaciones no podrán realizarse
frágil y de resultados más
al considerarse erróneas.
impredecibles.
Ejemplos: Java, Python.
Ejemplos: C, JavaScript.
Java
JavaScript
int a = 4 + ’7’; // 59 (7 is 55 in ASCII)
int b = 4 * "7"; // compilation error 4 + ’7’; // ’47’
int c = 2 + true; // compilation error 4 * ’7’; // 28
int d = false - 3; // compilation error 2 + true; // 3
false - 3; // -3
Dimensiones de tipado
Las dos dimensiones son independientes.
class Duck:
def quack(self):
print("Quack, quack!")
def fly(self):
print("Flap, Flap!")
class Person:
def quack(self):
print("I’m Quackin’!")
def fly(self):
print("I’m Flyin’!")
class Duck {
Incumple restricción (2)
public void quack() {
System.out.println("Quack, quack!"); class DuckTyping {
} // la función tiene un tipo declarado
public void fly() { public static void in_the_forest
System.out.println("Flap, Flap!"); (Duck mallard) {
} mallard.quack();
} mallard.fly();
}
class Person {
public void quack() { public static void main(String[] args){
System.out.println("I’m Quackin’!"); in_the_forest(new Duck());
} in_the_forest(new Person()); // ERROR
public void fly() { // Person no es compatible con Duck
System.out.println("I’m Flyin’!"); }
}
}
Existen ciertas operaciones con los tipos que pueden ser válidas,
pero si no hay una seguridad absoluta de que lo son entonces es
mejor no permitirlas.
La idea subyacente es que es mejor detectar los posibles errores en
tiempo de compilación y no en tiempo de ejecución (ya que son una
mayor molestia tanto para usuarios como para desarrolladores).
Ejemplo
Duck y Person tienen definidos los métodos quack() y fly()
por lo que podrı́an sin problemas ser pasados por parámetro a la
función in the forest(Object mallard).
Pero la función admite objetos de todo tipo, no todos los objetos
tienen los métodos quack() y fly(), por lo que la función falları́a
en tiempo de ejecución cada vez que pasaramos uno de estos objetos.
class Duck {
public void quack() { class DuckTyping {
System.out.println("Quack, quack!"); public static void in_the_forest
} (Object mallard) {
public void fly() { ((Duck)mallard).quack();
System.out.println("Flap, Flap!"); ((Duck)mallard).fly();
} }
}
public static void main(String[] args){
class Person { in_the_forest(new Duck()); // OK
public void quack() { in_the_forest(new Person()); // ERROR
System.out.println("I’m Quackin’!"); }
}
public void fly() {
System.out.println("I’m Flyin’!"); Pueden dar lugar a
} errores en ejecución
}
interface QuackAndFly {
public void quack();
public void fly(); class DuckTyping {
} public static void in_the_forest
class Duck implements QuackAndFly { (QuackAndFly mallard) {
public void quack() { mallard.quack();
System.out.println("Quack, quack!"); mallard.fly();
} }
public void fly() {
System.out.println("Flap, Flap!"); public static void main(String[] args){
} in_the_forest(new Duck()); // OK
} in_the_forest(new Person()); // OK
class Person implements QuackAndFly { }
public void quack() { }
System.out.println("I’m Quackin’!");
}
public void fly() { Solución más orientada a
System.out.println("I’m Flyin’!"); objetos. Funciona gracias
} a la Ligadura Dinámica
}
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 212
Abstracción y Encapsulamiento
Modularidad Funcionamiento
Jerarquı́a Esencia del Diseño OO
Polimorfismo Métodos sin Ligadura Dinámica
Tipificación
Ligadura Dinámica
Índice
1 Abstracción y Encapsulamiento
2 Modularidad
3 Jerarquı́a
4 Polimorfismo
5 Tipificación
6 Ligadura Dinámica
Funcionamiento
Esencia del Diseño OO
Métodos sin Ligadura Dinámica
E. Mosqueira Rey (Coordinador) Tema 3: Propiedades Básicas de la OO 213
Abstracción y Encapsulamiento
Modularidad Funcionamiento
Jerarquı́a Esencia del Diseño OO
Polimorfismo Métodos sin Ligadura Dinámica
Tipificación
Ligadura Dinámica
Ligadura dinámica
Ligadura
Proceso que se encarga de ligar o relacionar la llamada a un método (o
mensaje) con el código que se ejecuta finalmente.
Tipos de ligadura
clase Figura
public abstract class Figura {
public abstract double area();
public abstract double perimetro();
}
public class Circulo extends Figura {
public double area() { return Math.PI*radio*radio; }
public double perimetro() { return 2*Math.PI*radio; }
// ...
}
class Rectangulo extends Figura {
public double area() { return base*altura; }
public double perimetro() { return (base*2)+(altura*2); }
// ...
}
clase Figura
public static double sumaAreas(List<? extends Figura> l) {
double suma = 0;
for (Figura f : l) {
suma += f.area();
}
La llamada a area ejecuta
return suma;
} distintos métodos según el
tipo dinámico de la figura que
estamos tomando de la lista
clase Cuadrado
class Cuadrado extends Figura {
private double lado;
clase Cuadrado
List<Figura> l = new ArrayList<>();
l.add(new Rectangulo(2, 3));
l.add(new Circulo(3));
l.add(new Rectangulo(5, 4));
l.add(new Cuadrado(2));
Propiedades:
Herencia: usada para describir la relación entre las figuras.
Polimorfismo: usado para crear una lista de distintos tipos de figuras.
Ligadura dinámica: usada al llamar al método area.
Ventajas:
Estamos creando código (sumaAreas) que será capaz de trabajar,
sin modificarlo, con código futuro que aún no está escrito
(Cuadrado) ⇒ Flexibilidad, Escalabilidad, Extensibilidad, etc.
El método sumaAreas desconoce con qué clases trabaja, lo único
que sabe es que son figuras ⇒ Abstracción, Seguridad, Facilidad
de mantenimiento, etc.
class Padre {
public static void metodoEstatico()
{ System.out.println("Padre");}
}
class Hijo extends Padre {
public static void metodoEstatico()
{ System.out.println("Hijo"); }
}
class Estaticos {
public static void main(String[] args) {
Padre p = new Hijo();
p.metodoEstatico();
}
}
class Padre {
public final void metodoFinal() {
System.out.println("Padre");
}
}
class Hijo extends Padre {
public final void metodoFinal() {
System.out.println("Hijo");
}
}
class Finales {
public static void main(String[] args) {
Padre p = new Hijo();
p.metodoFinal();
}
}
Clases finales
Clases que impiden ser extendidas a través de herencia.
Métodos finales
Métodos que impiden que las subclases los sobrescriban.
Clases finales
Las clases finales limitan la posible extensibilidad del código y deben
por lo general evitarse salvo situaciones puntuales ⇒ Las clases
inmutables se definen finales para asegurarse de que las subclases no
rompen dicha inmutabilidad.
Métodos finales
Los métodos finales también limitan algo esencial en la orientación a
objetos como es la sobrescritura de métodos.
En ocasiones no queremos limitar la posibilidad de que una clases
tenga subclases, sino solo limitar la posibilidad de sobrescribir ciertos
métodos ⇒ Patrón Método-Plantilla (Tema 6).