Ir al contenido principal

Lambda expressions JAVA


Código fuente articulo:
https://www.dropbox.com/s/pzw44ot0ji2metl/Lambda.zip?dl=0

Una nueva funcionalidad introducida desde la versión 8 de JAVA son las Lambda expressions, estas son expresiones que simplifican la escritura de código haciendo mas concisa la implementación de algunos métodos. Piensa en clases anónimas pero aun mas simples, el problema es que al simplificar se agregan nuevos símbolos, un par de clases de utilidad y se deben tener en cuenta reglas como el numero de métodos que debe especificar la interfaz a implementar.

Para iniciar diremos que para usar una expresión Lambda se necesita definir una interfaz

interface VerificarPersona {
    boolean verificar(Persona p);
  }

Esta interfaz es llamada interfaz funcional porque solo tiene una función, se decir si le agregas otra función la arruinas de forma miserable, porque el compilador no sabría cual de las dos funciones estarías implementando con la expresión Lambda.

Para ilustrar esto de la función lambda vamos a imitar un ejemplo del JAVA TUTORIAL. este ejemplo lo que busca es crear una función capas de filtrar personas según criterios variables. La clase Persona luce así.

public class Persona{
  public enum Sexo {
    MASCULINO,
    FEMENINO
  }

  String nombre;
  Integer edad;
  Sexo genero;

}

Para el filtro se define un método imprimirPersonas que imprime los nombre de ciertas personas que cumplen con un criterio de búsqueda, el método se ve así

  public static void imprimirPersonas(
         List<Persona> grupo, VerificarPersona filtro) {
   for (Persona p : grupo) {
     if (filtro.verificar(p)) {
       System.out.println(p.nombre);
     }
   }
 }

Para cumplir con su tarea este método recibe; una lista de personas y un filtro que utilizara para imprimir a aquellas personas que cumplan con el criterio, el filtro que es donde vamos a utilizar una función Lambda. Si te fijas el filtro es de tipo VerificarPersona y esta interface tiene un método verificar que regresa un valor entero, por consiguiente nuestra implementación sera la de una expresión lógica que regrese un valor falso o verdadero.

la primer expresión que vamos a construir filtrara a los hombres, mayores de 18 años y se vera así

 (Persona p) -> p.getGenero() == Persona.Sexo.MASCULINO
                    && p.getEdad() >= 18
                 
ahora una que filtre a las mujeres menores de 16 años

(Persona p) -> p.getGenero() == Persona.Sexo.FEMENINO
                    && p.getEdad() <16

ahora debemos pasarle estas expresiones al método así;

para el caso uno

imprimirPersonas(
            listaPersonas,
           (Persona p) -> p.getGenero() == Persona.Sexo.MASCULINO
                    && p.getEdad() >= 18
    );

para el caso dos

imprimirPersonas(
            listaPersonas,
         (Persona p) -> p.getGenero() == Persona.Sexo.FEMENINO
                    && p.getEdad() <16
    );

Ahora si miras la interface VerificarPersona es bastante desabrida y cumple un rol muy genérico, para no tener que crear estas interfaces huecas JAVA ofrece una interface mas genérica llamada Predicate<T>, un predicado es una función que regresa un valor falso o verdadero, como se quiere poder utilizar con cualquier clase Predicate<T> tiene un valor genérico para el definir el tipo de objeto que vamos a utilizar para obtener el valor falso o verdadero. La nueva función imprimir se llamara imprimirPersonasPredicado y se vera así.

 public static void imprimirPersonasPredicado (
         List<Persona> grupo, Predicate<Persona> filtro) {
   for (Persona p : grupo) {
     if (filtro.test(p)) {
     System.out.println(p.nombre);
     }
   }
 }

Si te fijas el Predicate<Persona> remplazo a VerificarPersona y la funciona verificar por la función test. Todo lo demás sigue igual y lo mejor la implementacion de los filtros no cambio mira ahora como quedan

para el caso uno

imprimirPersonasPredicado (
            listaPersonas,
           (Persona p) -> p.getGenero() == Persona.Sexo.MASCULINO
                    && p.getEdad() >= 18
    );

para el caso dos


imprimirPersonasPredicado (
            listaPersonas,
         (Persona p) -> p.getGenero() == Persona.Sexo.FEMENINO
                    && p.getEdad() <16
    );


Ahora supón que quieres en adición a la funcionalidad de filtro quieres llamar un método diferente para imprimir el nombre según el filtro aplicado. Para este fin se puede utilizar la clase Consumer<T>, Consumer es un comodín que se puede remplazar por cualquier método de la clase definido en <T> . Supongamos que tenemos dos nuevos métodos en la clase persona.

public void imprimirHombre() {
   System.out.println("Sr. "+nombre);
 }

public void imprimirMujer() {
   System.out.println("Sra. "+nombre);
 }

Y los queremos invocar según sea el caso para esto debemos modificar el método de filtro para que acepte un Consumer<T>, en nuestro caso un Consumer<Persona>.

public static void procesarPersonas(
         List<Persona> grupo,
         Predicate<Persona> filtro,
         Consumer<Persona> bloque) {
   for (Persona p : grupo) {
     if (filtro.test(p)) {
     bloque.accept(p);
     }
   }
 }

ahora nuestros filtros implementados se verán así;

procesarPersonas(
listaPersonas,
p -> p.getGenero() == Persona.Sexo.MASCULINO
            && p.getEdad() >= 18,
            p -> p.imprimirHombre()
    );

procesarPersonas(
listaPersonas,
p -> p.getGenero() == Persona.Sexo.FEMENINO
            && p.getEdad() <16,
            p -> p.imprimirMujer()
    );


Ahora extendamos un poco mas, supón que quieres recuperar el genero para imprimirlo, pero te das cuenta que Consumer<T>, no regresa ningún valor :'( , por lo que vas a necesitar otro comodín que te ayude a recuperar el valor del genero, para esto existe Function<X,Y> esta clase te permite especificar que vas a llamar un método de la clase X y que esperas recibir en respuesta un objeto de la clase Y.

para esto le agregaremos un nuevo método a persona;

public String nombreGenero(){
return this.genero.name();
}

Ahora agregaremos un método que soporte la nueva funcionalidad.

 public static void procesarPersonasConFunciones(
List<Persona> grupo,
         Predicate<Persona> filtro,
         Function<Persona, String> mapper,
         Consumer<String> bloque
         ) {
   for (Persona p : grupo) {
     if (filtro.test(p)) {
       String data = mapper.apply(p);
       bloque.accept(data);
     }
   }
 }

y lo pondremos en uso

procesarPersonasConFunciones(listaPersonas, 
p -> p.getGenero() == Persona.Sexo.FEMENINO
            && p.getEdad() <16,
            p -> p.nombreGenero(),
            genero -> System.out.println(genero));



esta seria la versión final del ejemplo

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public class FiltroLambda {

interface VerificarPersona {
   boolean verificar(Persona p);
 }

public static void imprimirPersonas(
         List<Persona> grupo, VerificarPersona filtro) {
   for (Persona p : grupo) {
     if (filtro.verificar(p)) {
       System.out.println(p.nombre);
     }
   }
 }
 
public static void imprimirPersonasPredicado (
         List<Persona> grupo, Predicate<Persona> filtro) {
   for (Persona p : grupo) {
     if (filtro.test(p)) {
     System.out.println(p.nombre);
     }
   }
 }
 
public static void procesarPersonas(
         List<Persona> grupo,
         Predicate<Persona> filtro,
         Consumer<Persona> bloque) {
   for (Persona p : grupo) {
     if (filtro.test(p)) {
     bloque.accept(p);
     }
   }
 }
 
public static void procesarPersonasConFunciones(
List<Persona> grupo,
         Predicate<Persona> filtro,
         Function<Persona, String> mapper,
         Consumer<String> bloque
         ) {
   for (Persona p : grupo) {
     if (filtro.test(p)) {
       String data = mapper.apply(p);
       bloque.accept(data);
     }
   }
 }
 
public static void main(String[] args) {

List<Persona> listaPersonas= new ArrayList<>();
listaPersonas.add(new Persona("Juan", 12, Persona.Sexo.MASCULINO));
listaPersonas.add(new Persona("Maria", 14, Persona.Sexo.FEMENINO));
listaPersonas.add(new Persona("Pedro", 45, Persona.Sexo.MASCULINO));
listaPersonas.add(new Persona("Martin", 21, Persona.Sexo.MASCULINO));
listaPersonas.add(new Persona("Adriana", 16, Persona.Sexo.FEMENINO));
listaPersonas.add(new Persona("Natalia", 27, Persona.Sexo.FEMENINO));

imprimirPersonas(
           listaPersonas,
          (Persona p) -> p.getGenero() == Persona.Sexo.MASCULINO
                   && p.getEdad() >= 18
   );

imprimirPersonas(
           listaPersonas,
        (Persona p) -> p.getGenero() == Persona.Sexo.FEMENINO
                   && p.getEdad() <16
   );


imprimirPersonasPredicado (
           listaPersonas,
           p -> p.getGenero() == Persona.Sexo.MASCULINO
                   && p.getEdad() >= 18
   );


imprimirPersonasPredicado (
           listaPersonas,
           p -> p.getGenero() == Persona.Sexo.FEMENINO
                   && p.getEdad() <16
   );

procesarPersonas(
listaPersonas,
p -> p.getGenero() == Persona.Sexo.MASCULINO
            && p.getEdad() >= 18,
            p -> p.imprimirHombre()
    );

procesarPersonas(
listaPersonas,
p -> p.getGenero() == Persona.Sexo.FEMENINO
            && p.getEdad() <16,
            p -> p.imprimirMujer()
    );

procesarPersonasConFunciones(listaPersonas, 
p -> p.getGenero() == Persona.Sexo.FEMENINO
            && p.getEdad() <16,
            p -> p.nombreGenero(),
            genero -> System.out.println(genero));



}

class Persona{
 enum Sexo {
   MASCULINO,
   FEMENINO
 }

 String nombre;
 Integer edad;
 Sexo genero;
 
 
public Persona(String nombre, Integer edad, Sexo genero) {
super();
this.nombre = nombre;
this.edad = edad;
this.genero = genero;
}
public void imprimirHombre() {
   System.out.println("Sr. "+nombre);
 }

public String nombreGenero(){
return this.genero.name();
}

public void imprimirMujer() {
   System.out.println("Sra. "+nombre);
 }

public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public Integer getEdad() {
return edad;
}
public void setEdad(Integer edad) {
this.edad = edad;
}
public Sexo getGenero() {
return genero;
}
public void setGenero(Sexo genero) {
this.genero = genero;
}  
 
}

Este es el primer paso con expresiones Lambda, mas adelante seguiré publicando ejemplos mas avanzados, mientras tanto, cuéntanos en los comentarios que te gustaría hacer con expresiones Lambda

Comentarios

Entradas populares de este blog

Conectarse al LDAP (directorio activo) utilizando JAVA

Un LDAP es un sistema de autenticación estándar utilizado por muchas compañías para controlar el acceso a aplicaciones y recursos. Por lo general se espera que cualquier nueva aplicación haga uso del LDAP para realizar la autenticación y controlar los permisos en forma unificada, la seguridad es transversal a todos los procesos que realiza una organización. Una vez regado el cuento a lo que vinimos, como conectarse a un LDAP. Lo primero que debes saber es que no se requieren librerías adicionales, JAVA en su distribución estandar ya cuenta con todo lo que necesitas. primero tres siglas que tienes que tener en cuanta. CN  = Common Name OU  = Organizational Unit DC  = Domain Component Para conectarse primero necesitas es instanciar un Objeto de la clase LdapContext, este se encargara de manejar la conexión al LDAP y las peticiones que se hagan al mismo. por consiguiente necesitara que le entregues una serie de propiedades de conexión. Esto lo haras con un Map de...

Clases anónimas JAVA (Anonymous Classes)

Código fuente articulo: https://www.dropbox.com/s/pzw44ot0ji2metl/Lambda.zip?dl=0 Las Clases anónimas en JAVA son una solución rápida para implementar una clase que se va utilizar una vez y de forma inmediata. Por ejemplo el  EventHandler  para un botón se puede implementar en la misma asignación valiendonos de la interfaz  EventHandler  que ya esta definida. Pero mejor vamos con un ejemplo mas simple. De la definición anterior concluimos dos cosas la primera es que para crear una clase anónima es necesario haber definido una interfaz, una clase o una clase abstracta. La clase anónima lo que hará sera implementar la interfaz definida o sobre escribir los métodos definidos. Para ilustrar esto utilizaremos el ejemplo del JAVA Tutorial https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html . en este ejemplo tenemos que implementar clases que cumpliendo con la interfaz Saludo sean capaces de saludar en diferentes idiomas. El paso uno ...

Paralelismo en JAVA Executors (ExecutorService, Callables y Futures).

Código fuente articulo: https://www.dropbox.com/s/jci67120hmd0uce/Paralelismo.zip?dl=0 Para manejo de concurrencia Java desde la versión 5 presento el Concurrency API  este presento una mejora substancial en el manejo de hilos y procesos en paralelo, antes solo contabas con Thread y Runnable. lo que te obligaba a controlar la creación y el numero de hilos de ejecución, no te entregaba un resultado del procesamiento y no te dejaba controlar las Excepciones que lanzara un hilo...un hilo se lanzaba y amenos que le enviaras un un CallBack perdías todo contacto con el. Para solucionar este problema se crearon dos tipos de objetos Callables y Futures. estos dos te permiten encapsular una tarea asignándole un tipo de Objeto que sera el valor de retorno y hacer seguimiento a las tareas que ejecuto en paralelo pudiendo preguntar si ya termino, que resultado lanzo y que excepciones ocurrieron. Arranquemos con la implementación de Callable, Callable es una interfaz que te permite de...