Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Java Avanzado Clase 12

Descargar como pptx, pdf o txt
Descargar como pptx, pdf o txt
Está en la página 1de 19

JAVA AVANZADO

Clase 12
Programación Funcional
Les damos la bienvenida
Vamos a comenzar a grabar la clase
Clase 12 Clase 13

CLASE 12 - Programación Funcional CLASE 13 - Introducción a Spring Boot


● Qué es? ● Qué es?
● Functional Interface ● Spring vs Spring Boot
● Lambdas ● Modelo de Proyecto
PROGRAMACIÓN FUNCIONAL
La programación funcional es un paradigma de programación basado en funciones matemáticas,
los lenguajes de programación funcional son aquellos lenguajes donde las variables no tienen
estado. No hay cambios en estas a lo largo del tiempo, son inmutables (no pueden cambiarse los
valores a lo largo de la ejecución), además los programas se estructuran componiendo expresiones
que se evalúan como funciones.
Uno de los ejemplos más comunes de la programación funcional es que las instrucciones cíclicas
como el for, el while y el do while no existen. Todo se procesa usando recursividad y funciones de
alto orden. Sin embargo en Java vamos a seguir teniendo las dos opciones, vamos a continuar
teniendo nuestras funciones cíclicas que siempre hemos usado como programadores de Java y
ahora también estas nuevas funciones de alto orden que nos van a permitir iterar a través de una
serie de colecciones y conjuntos de datos, apoyándonos en las expresiones lambda y en una serie
de métodos que son incorporados en las versiones de java.

En los lenguajes estrictamente funcionales no hay tipos de datos. En Java no vamos a tener
exactamente lo mismo, pero por medio de las expresiones lambda vamos a poder hacer uso de la
inferencia de tipos o más bien dentro de las expresiones lambda vamos a poder prescindir de
declarar los tipos de datos y vamos a dejar ese trabajo al compilador por medio de la inferencia de
tipos.

Para lograr todo esto Java ha incorporado nuevas características como las expresiones lambda,
métodos referenciados y las interfaces funcionales.
- Conocer cuántos números mayores a 10 hay en la Lista

List<Integer> numeros = List.of(11, 8, 9, 15, 39, 1, 4, 83);

- Resolvemos la problemática con ciclos y condicionales, enfoque declarativo

List<Integer> numeros = List.of(11, 8, 9, 15, 39, 1, 4, 83);

int contador = 0;

for (int numero: numeros) { if(nuemero > 10){ contador ++; } }


System.out.println(contador);
- El enfoque imperativo sería el siguiente

// Imperativo
Long resultado = numeros.stream().filter(num -> num > 10).count():

System.out.println(resultado);

Como podemos observar con la programación funcional las líneas de código se reducen y nuestro
código se muestra más legible.

En la implementación imperativa se podría concluir que es funcional, ya que delega el control de


flujo y condiciones a las funciones filter y count.
EXPRESIONES LAMBDAS
Una expresión lambda está caracterizada o su sintaxis es:

parámetro → cuerpo de la expresión lambda


Una expresión lambda representa el método abstracto de una interfaz funcional, una interfaz
funcional es aquella que solo tiene un método abstracto, puede tener cualquier cantidad de
métodos default o estáticos, pero solamente puede tener un método abstracto. Este método puede
ser representado por una expresión lambda.

// Clase principal
public class lambdaFunction {

// Se declara la interface
interface operacion {
// el metodo abstracto
public double suma(double x, double y);
}

public static void main(String[] args) {


// Expresion lambda
operacion l = (x, y) -> x + y;
System.out.println(l.suma(8, 30));
}

}
INTERFACES FUNCIONALES
Una interfaz funcional es aquella que solo tiene un método abstracto, podemos utilizar métodos
default, métodos estáticos y métodos heredados de la clase object y declararlos como métodos
abstractos.

// Clase principal
public class lambdaFunction {
// Anotacion para declara la interface
@FunctionalInterface
interface operacion {
// el metodo abstracto
public double suma(double x, double y);
}
public static void main(String[] args) {
// Expresion lambda
operacion l = (x, y) -> x + y;
System.out.println(l.suma(8, 30));
} }
Si la interfaz que estamos declarando contiene la Anotación @FunctionalInterface y esta no cumple con
los criterios para que sea una interfaz funcional nos dará un error de compilación, esto nos ayuda
y es una buena práctica para desarrollar correctamente. Cabe mencionar que una interfaz con un
solo método abstracto como lo hemos comentado sigue siendo una interfaz funcional aunque no
tenga la Anotación @FunctionalInterface.

Métodos referenciados
Hasta ahora lo que sabemos y conocemos de la programación funcional es que las expresiones
lambda sirven para reemplazar los métodos anónimos en el caso de las interfaces funcionales.
Algunas veces sin embargo una expresión lambda no hace otra cosa más que llamar a un método
que ya existe, en estos casos podría ser más claro referirnos al método que ya existe por su
nombre.

Los métodos referenciados nos permiten referirnos a los métodos que ya existen mediante su
nombre.
import java.util.function.Consumer;

public class referenceMethods {

public static void main(String[] args) {

// utilizaremos en la interface Consumer que tiene un unico metodo


denominado accept
// el cual recibe un parametro y no devuelve nada
Consumer<String> consumidor = x -> System.out.println(x);
consumidor.accept("Bienvenido");
procesar(x -> System.out.println(x), "Bienvenido2");
// Pasamos como referencia una funcion que se encuentra en nuestro propio
programa
procesar(referenceMethods::saludar, "Bienvenido3");
}
public static <T> void procesar(Consumer<T> expresion, T mensaje) {
expresion.accept(mensaje);
}
// Disponemos de otro metodo en la misma clase que cumple con el patron
// de la expresion lambda (reciba un parametro y no devuelva nada)
public static void saludar(String saludo) {
System.out.println(saludo);
}

}
¿QUÉ SON LOS STREAMS?
Podríamos decir que los Streams son "envoltorios" de colecciones de datos que nos
permiten operar con estas colecciones y hacer que el procesamiento masivo de datos sea
rápido y fácil de leer. Algo importante a tener en cuenta es que los Streams no almacenan
datos y no son una estructura de datos en sí. Tampoco modifican la fuente de datos
subyacente. Solo realizan operaciones simples o concatenadas sobre ellos.

¿QUÉ NOS PERMITEN HACER?


Generalmente cuando trabajamos con colecciones filtramos, calculamos y realizamos
muchos tipos de operaciones sobre los datos. Los Streams nos permiten a través del
paradigma funcional abstraernos del cómo programar esas operaciones y sólo centrarnos en
que resultado se espera y escribirlo de una manera muy declarativa.
STREAMS EN JAVA

Los Streams en java son un nuevo modelo de datos que nos permite tratar las colecciones como si de etapas
de un proceso ETL (“Extract Transform and Load”) se tratara. Esto nos permite utilizar las funciones Map
y Reduce tan comunes en esos procesos, especialmente en la etapa de transformación.

En java 8, toda colección tiene un método stream() que transformará dicha estructura en Stream. Por
ejemplo vamos a crear una instancia llamada “streangs” de Stream de Strings.

[java] List<String> strings = …;


Stream<String> streangs = strings.stream();
[/java]
Foreach: Aplica una función a cada uno de los elementos. Este método es terminal, no se puede encadenar con otra
etapa del Stream.

[java] streangs.forEach(s -> System.out.println(s)); // expresión Lambda


streangs.forEach(System.out::println); // referencia a función
// Imprime por pantalla cada una de las cadenas
[/java]
Map: Transforma cada uno de los objetos que contiene la colección, pudiendo incluso cambiar el tipo de Stream.

[java] streangs.map(s -> s + "_MAPPED");

// Añadiría la cadena "_MAPPED" a todos los elementos

streangs.map(s -> Integer.parse(s));

// Transforma cada elemento en un Integer // A partir de aquí el Stream<String> cambie a Stream<Integer>


Reduce: Transforma la colección en único objeto del mismo tipo después de aplicar una función de agregación a cada
elemento.
streangs.reduce("", // Valor inicial (s1, s2) -> s1 + " " + s2); // Concatena cada elemento con el agregado

// Con esto obtendríamos una única cadena concatenando todos sus elementos
Filter: Filtra los elementos que satisfagan una determinada condición: streangs.filter(s -> s.length() > 5); // Solo
mantenemos cadenas de longitud mayor a 5

Collect: El paso final de la transformación del Stream, nos permitirá por ejemplo volverlo a convertir a una lista
estándar.

List<String> result = streangs.collect(Collectors.toList());

Salvo los métodos finales como son collect, reduce o forEach todos se pueden ir encadenando como si fueran tuberías,
creando un proceso de transformación más complejo./ Obtenemos las cadenas ordenadas que representen números pares
List<String> even = strings.stream()
.map(string -> Integer.valueOf(s))
.filter(integer -> integer % 2 == 0)
.distinct()
.sorted()
.map(integer::toString)
.collect(Collectors.toList());

A modo de conclusión, podemos decir que con estos streams tenemos una nueva manera de procesar colecciones de datos
respecto a las tradicionales sentencias de control como pueden ser bucles y condiciones. Además, también nos proporcionan
la ventaja de poder “exprimir” más a los procesadores, utilizando para ello los parallelStream, que utilizan varios hilos de
ejecución en cada etapa de procesamiento.
Recordá:
● Revisar la Cartelera de Novedades.
● Hacer tus consultas en el Foro.

Todo en el Aula Virtual.


Muchas gracias por tu atención.
Nos vemos pronto

También podría gustarte