-
El estilo funcional introducido en Java 8 nos ayuda a reducir la brecha entre la lógica empresarial y el código. Nos permite contar la historia de forma natural a un nivel superior. En lugar de decir cómo quieres hacerlo, puedes decir lo que quieres hacer.
-
El código se vuelve más limpio y conciso.
-
Las funciones de alto orden nos permiten:
- Enviar funciones a otras funciones
- Crear funciones dentro de otras funciones.
- Funciones de retorno de otras funciones
Esta es una gran ventaja para Java, donde necesitamos enviar, crear y devolver objetos para hacerlo. Podremos escribir código que sea más confiable, enfocado y más fácil de reutilizar.
-
Gracias a las lambdas, podemos hacer cálculos diferidos. Cuando se envía una expresión lambda como argumento de un método, el compilador la evaluará cuando se llame en el método. Esto es diferente de los argumentos de los métodos normales, que se evalúan inmediatamente.
-
Lambdas hace que escribir pruebas unitarias sea divertido. Nos permiten crear pruebas livianas, limpias, de tamaño pequeño y rápidas de escribir. Podemos eliminar el código bajo prueba usando lambdas. Esto nos permite probar cómo afectarán al código todo tipo de escenarios.
-
Nuevos patrones para aprender.
-
¡Y mucho más!
break
, cambian drásticamente el comportamiento del bucle, lo que nos obliga a comprender no solo lo que el código intenta lograr, continue
sino return
también cómo funciona el bucle. Ahora veremos cómo podemos transformar bucles en código más conciso y legible.
¡Que comience la codificación!
Trabajaremos con artículos. Un artículo tiene un título, autor y varias etiquetas.private class Article {
private final String title;
private final String author;
private final List<String> tags;
private Article(String title, String author, List<String> tags) {
this.title = title;
this.author = author;
this.tags = tags;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public List<String> getTags() {
return tags;
}
}
Cada ejemplo contendrá una solución tradicional que usa bucles y una solución que usa las nuevas características de Java 8. En el primer ejemplo, queremos encontrar el primer artículo de la colección con la etiqueta "Java". Echemos un vistazo a una solución que utiliza un bucle.
public Article getFirstJavaArticle() {
for (Article article : articles) {
if (article.getTags().contains("Java")) {
return article;
}
}
return null;
}
Ahora resolvamos el problema usando operaciones de Stream API.
public Optional<Article> getFirstJavaArticle() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.findFirst();
}
Muy bueno, ¿no? Primero usamos la operación filter
para buscar todos los artículos con la etiqueta "Java", luego la usamos findFirst()
para obtener la primera aparición. Dado que las transmisiones son diferidas y el filtro devuelve una secuencia, este enfoque solo procesará elementos hasta que encuentre la primera coincidencia. Ahora pongamos todos los artículos etiquetados como "Java" en lugar de solo el primero. Primero la solución usando bucles.
public List<Article> getAllJavaArticles() {
List<Article> result = new ArrayList<>();
for (Article article : articles) {
if (article.getTags().contains("Java")) {
result.add(article);
}
}
return result;
}
Solución mediante operaciones de flujo.
public List<Article> getAllJavaArticles() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.collect(Collectors.toList());
}
En este ejemplo, utilizamos una operación collect
para acortar el flujo resultante, en lugar de declarar una colección y agregar explícitamente las entradas que coinciden. Hasta ahora, todo bien. Es hora de ver ejemplos que harán que Stream API realmente brille. Agrupemos todos los artículos por autor. Como es habitual, empezamos resolviéndolo mediante bucles:
public Map<String, List<Article>> groupByAuthor() {
Map<String, List<Article>> result = new HashMap<>();
for (Article article : articles) {
if (result.containsKey(article.getAuthor())) {
result.get(article.getAuthor()).add(article);
} else {
ArrayList<Article> articles = new ArrayList<>();
articles.add(article);
result.put(article.getAuthor(), articles);
}
}
return result;
}
¿Podemos encontrar una solución limpia a este problema utilizando operaciones de flujo?
public Map<String, List<Article>> groupByAuthor() {
return articles.stream()
.collect(Collectors.groupingBy(Article::getAuthor));
}
¡Asombroso! Al utilizar una operación groupingBy
y una referencia de método getAuthor()
, obtenemos un código limpio y legible. Ahora busquemos el resto de las etiquetas utilizadas en la colección. Comencemos con un ejemplo de bucle:
public Set<String> getDistinctTags() {
Set<String> result = new HashSet<>();
for (Article article : articles) {
result.addAll(article.getTags());
}
return result;
}
Bien, echemos un vistazo a cómo podemos resolver esto usando operaciones de flujo:
public Set<String> getDistinctTags() {
return articles.stream()
.flatMap(article -> article.getTags().stream())
.collect(Collectors.toSet());
}
¡Fresco! flatmap
nos ayuda a aplanar la lista de etiquetas en un único flujo de resultados, que luego usamos collect
para crear el conjunto de retorno.
GO TO FULL VERSION