W } P u ] v K ] v K i } µ } î ì í ò l î ì í...

6
Programación Orientada a Objetos Curso 2016/2017 1/6 ANEXO Tema 5: Ejercicios Java 8 Previo Descárgate de la sección de prácticas de la página web de la asignatura (dis.um.es/docencia/poo) el proyecto “PruebasJava8” e impórtalo en Eclipse. Este proyecto contiene la implementación del problema de la subasta. El enunciado de este problema también lo puedes encontrar en la web (Sesión 4). A la clase Usuario se le han añadido dos atributos que no se contemplan en el problema de la subasta (fecha de nacimiento y correo electrónico) para favorecer la definición de los ejercicios relacionados con las nuevas propiedades de Java 8. Caso de estudio Supongamos que al administrador del sistema de subastas le interesa disponer de una funcionalidad que le permita ejecutar cualquier tipo de acción (por ejemplo, enviar un correo electrónico) sobre todos los usuarios registrados que satisfagan un criterio (por ejemplo, aquellos que sea su cumpleaños). Vamos a analizar diferentes alternativas para implementar esta funcionalidad. Entre estas alternativas encontraremos primero soluciones “Java 7” y posteriormente la aplicación de las nuevas características de Java 8. Enfoque 1: Definir métodos de búsqueda El enfoque más simple es implementar en una clase de utilidades (clase Utils) tantos métodos diferentes como criterios de búsqueda y acciones queramos aplicar sobre los usuarios registrados. Por ejemplo, public class Utils { public static void imprimirUsuariosMayoresDe(List<Usuario> usuarios, int edad){ for (Usuario usuario : usuarios){ if (usuario.getEdad() >= edad) System.out.println(usuario); } } public static void imprimirUsuariosEnRangoEdad(List<Usuario> usuarios, int edadMin, int edadMax){ for (Usuario usuario : usuarios){ if (usuario.getEdad() >= edadMin && usuario.getEdad() <= edadMax) System.out.println(usuario); } } } No obstante, este enfoque no es un buen diseño dado que implementamos métodos prácticamente iguales y las condiciones de búsqueda pueden ser muy amplias y pueden ir cambiando. CRITERIO ACCIÓN

Transcript of W } P u ] v K ] v K i } µ } î ì í ò l î ì í...

Programación Orientada a Objetos Curso 2016/2017

1/6

ANEXO Tema 5: Ejercicios Java 8 Previo Descárgate de la sección de prácticas de la página web de la asignatura (dis.um.es/docencia/poo) el proyecto “PruebasJava8” e impórtalo en Eclipse. Este proyecto contiene la implementación del problema de la subasta. El enunciado de este problema también lo puedes encontrar en la web (Sesión 4).

A la clase Usuario se le han añadido dos atributos que no se contemplan en el problema de la subasta (fecha de nacimiento y correo electrónico) para favorecer la definición de los ejercicios relacionados con las nuevas propiedades de Java 8.

Caso de estudio

Supongamos que al administrador del sistema de subastas le interesa disponer de una funcionalidad que le permita ejecutar cualquier tipo de acción (por ejemplo, enviar un correo electrónico) sobre todos los usuarios registrados que satisfagan un criterio (por ejemplo, aquellos que sea su cumpleaños).

Vamos a analizar diferentes alternativas para implementar esta funcionalidad. Entre estas alternativas encontraremos primero soluciones “Java 7” y posteriormente la aplicación de las nuevas características de Java 8.

Enfoque 1: Definir métodos de búsqueda El enfoque más simple es implementar en una clase de utilidades (clase Utils) tantos métodos diferentes como criterios de búsqueda y acciones queramos aplicar sobre los usuarios registrados. Por ejemplo, public class Utils { public static void imprimirUsuariosMayoresDe(List<Usuario> usuarios, int edad){ for (Usuario usuario : usuarios){ if (usuario.getEdad() >= edad) System.out.println(usuario); } } public static void imprimirUsuariosEnRangoEdad(List<Usuario> usuarios, int edadMin, int edadMax){ for (Usuario usuario : usuarios){ if (usuario.getEdad() >= edadMin && usuario.getEdad() <= edadMax) System.out.println(usuario); } } } No obstante, este enfoque no es un buen diseño dado que implementamos métodos prácticamente iguales y las condiciones de búsqueda pueden ser muy amplias y pueden ir cambiando.

CRITERIO

ACCIÓN

Programación Orientada a Objetos Curso 2016/2017

2/6

Enfoque 2: Definir los criterios de búsqueda en una clase separada Una mejora al código anterior consiste en definir cada criterio de búsqueda en una clase separada. De esta manera, podemos definir un método que imprima los usuarios que cumplan el criterio que se establece como parámetro:

public static void imprimirUsuarios(List<Usuario> usuarios, Criterio criterio){ for (Usuario usuario : usuarios) if (criterio.test(usuario)) System.out.println(usuario); }

El método imprimirUsuarios es un ejemplo de aplicación del patrón estrategia. El criterio de búsqueda (estrategia) se define en una interfaz que incluye el método test. Este método recibe como parámetro un objeto de tipo Usuario y devuelve un valor boolean que indica si el usuario cumple o no el criterio de búsqueda.

public interface Criterio { boolean test(Usuario usuario); }

De esta forma, tendremos que implementar tantas clases como criterios de búsqueda necesitemos. Por ejemplo, el criterio que seleccione los usuarios jóvenes, esto es mayores de edad y menores de 35 años.

public class UsuariosJovenes implements Criterio { @Override public boolean test(Usuario usuario) { return usuario.getEdad() >= 18 && usuario.getEdad() < 35; } }

Para imprimir los usuarios jóvenes, tendremos que pasar como parámetro al método imprimirUsuarios un objeto de la clase que implementa el criterio de búsqueda (o selección) de usuarios:

public class Programa { public static void main(String[] args) { LinkedList<Usuario> usuarios = new LinkedList<Usuario>(); ... //Se omite la creación de Usuarios e inserción en la lista Utils.imprimirUsuarios(usuarios, new UsuariosJovenes()); } }

Programación Orientada a Objetos Curso 2016/2017

3/6

Enfoque 3: Utilizar clases anónimas para definir los criterios El enfoque anterior tiene el inconveniente de que se implementan muchas clases, y algunas de ellas sólo se van a utilizar en un punto del código. En lugar de crear una clase por cada criterio, se pueden utilizar clases anónimas para especificar el criterio en el momento de hacer la llamada al método imprimirUsuarios:

Utils.imprimirUsuarios(usuarios, new Criterio(){ public boolean test(Usuario usuario){ return usuario.getEdad() >= 18 && usuario.getEdad() < 35; } });

Enfoque 4: Utilizar expresiones lambda para especificar el criterio de búsqueda (Java 8) Dado que la interfaz Criterio que hemos definido contiene un solo método (método test), es un ejemplo de interfaz funcional, que se introduce en Java 8. Una característica útil de este tipo de interfaz es que cuando se implementa se puede omitir el nombre del método y el tipo de la interfaz. Así, en lugar de utilizar una clase anónima, cuando se llama al método imprimirUsuarios, se puede pasar como parámetro una expresión lambda que especifica el comportamiento del método test de la interfaz. Utils.imprimirUsuarios(usuarios, usuario -> usuario.getEdad() >= 18 && usuario.getEdad() < 35);

Enfoque 5: Utilizar las interfaces funcionales predefinidas (interfaz Predicate) La librería de Java proporciona varias interfaces funcionales de utilidad en el paquete java.util.function, como por ejemplo, la interfaz Predicate<T>.

public interface Predicate<T> { boolean test(T elem); }

La definición de esta interfaz coincide con la definición de la interfaz Criterio que hemos implementado previamente (Enfoque 2), con la diferencia de que el predicado se define de forma genérica. Puesto que el lenguaje proporciona ya este tipo de datos, no merece la pena implementar un tipo tan sencillo en nuestra aplicación y podemos reutilizar el que nos ofrece la librería de Java. Así, la versión del método imprimirUsuario quedaría como sigue:

public static void imprimirUsuariosPredicado(List<Usuario> usuarios, Predicate<Usuario> criterio){ for (Usuario usuario : usuarios) if (criterio.test(usuario)) System.out.println(usuario); }

Programación Orientada a Objetos Curso 2016/2017

4/6

La forma de invocar a esta versión de imprimirUsuarios es igual a la utilizada en el enfoque 4, en el que hacíamos uso de las expresiones lambda.

Utils.imprimirUsuariosPredicado(usuarios, usuario -> usuario.getEdad() >= 18 && usuario.getEdad() < 35);

Enfoque 6: Utilizar expresiones lambda para especificar la acción (interfaz Consumer) La funcionalidad planteada en el caso de estudio requería que se pudiera aplicar cualquier acción sobre los usuarios que cumplieran un determinado criterio. En el código implementado hasta el momento, se puede especificar el criterio de selección de los usuarios, pero la acción a realizar es fija, imprimir el usuario. Podemos generalizar el método de manera que la acción que se va a aplicar, sobre los usuarios que cumplen el criterio de selección, también pueda ser establecida en el método. Para especificar la acción a realizar sobre los objetos seleccionados se puede utilizar otra de las interfaces funcionales predefinidas, la interfaz java.util.function.Consumer<T>. Esta interfaz contiene el método void accept(T t), que acepta un objeto y aplica alguna acción sobre él, sin retornar nada, por ejemplo, imprimir en la consola. Utilizando esta interfaz el método imprimirUsuarios se puede generalizar como sigue:

public static void procesarUsuarios(List<Usuario> usuarios, Predicate<Usuario> criterio, Consumer<Usuario> accion){ for (Usuario usuario : usuarios){ if (criterio.test(usuario)) accion.accept(usuario); } }

La forma de invocar a este método, utilizando expresiones lambda, para imprimir todos los usuarios jóvenes registrados sería:

Utils.procesarUsuarios(usuarios, usuario -> usuario.getEdad() >= 18 && usuario.getEdad() < 35, usuario -> System.out.println(usuario));

De forma alternativa, cuando el parámetro es una interfaz funcional, en lugar de una expresión lambda, también es posible pasar como parámetro una referencia a un método. Así, la llamada también podría hacerse de la siguiente forma: Utils.procesarUsuarios(usuarios, usuario -> usuario.getEdad() >= 18 && usuario.getEdad() < 35, System.out::println);

Programación Orientada a Objetos Curso 2016/2017

5/6

La invocación al método procesarUsuarios realiza las siguientes acciones: 1) Obtiene un usuario de la colección de usuarios. 2) Comprueba si cumple la condición establecida en el predicado (Predicate).

3) Ejecuta una acción sobre el objeto que cumple el predicado (Consumer), en este caso mostrarlo en la consola.

Enfoque 7: Utilizar streams para procesar los objetos de una colección En lugar de definir un método que se encargue de procesar los objetos de la colección utilizando un iterador, se puede utilizar un stream (java.util.Stream) de la colección (secuencia de objetos que contiene) y las operaciones que proporciona. Estas operaciones incluyen, entre otras, las funcionalidades para filtrar objetos (filter) y para aplicar una misma acción sobre todos los objetos de la secuencia (forEach). Así, no necesitaríamos la clase Utils y la funcionalidad para imprimir todos los usuarios jóvenes quedaría como sigue: usuarios.stream() .filter(usuario -> usuario.getEdad() >= 18 && usuario.getEdad() < 35) .forEach(usuario -> System.out.println (usuario));

Los streams también proporcionan, entre otras, una operación para obtener un nuevo stream con el resultado de la correspondencia del objeto original con otro (map). Así, la funcionalidad para imprimir el email de todos los usuarios jóvenes sería: usuarios.stream() .filter(usuario -> usuario.getEdad() >= 18 && usuario.getEdad() < 35) .map(usuario -> usuario.getNombre()) .sorted() .forEach(nombre -> System.out.println (nombre)); Ejercicios sobre streams

NOTA: En la sección anterior no se utilizan todas las operaciones soportadas por los streams (filter, sorted, map, forEach, anyMatch, noMatch, allMatch, count). Revisa las transparencias del Tema 5 de teoría para completar la información que aquí aparece y poder implementar los ejercicios propuestos.

Utiliza la clase PruebaStreams en el paquete pruebas para implementar la siguiente funcionalidad utilizando los streams y las expresiones lambda.

1) Cuenta los usuarios que tienen cuenta de correo en Gmail. Muestra el resultado en la consola.

2) Mostrar por la consola los nombres de usuarios que sean propietarios de subastas ordenados por orden alfabético inverso.

3) Mostrar por la consola los nombres de los productos cuyas subastas hayan recibido alguna puja ordenados alfabéticamente.

Programación Orientada a Objetos Curso 2016/2017

6/6

4) Mostrar por la consola el nombre de los productos de aquellas subastas que hayan recibido pujas superiores a 50 euros.

5) Consultar si hay usuarios que hayan ganado alguna subasta y que sean propietarios de subastas.

6) Crea un conjunto vacío de pujas, añade a ese conjunto todas las pujas que se hayan realizado en las subastas.