Sunday, March 23, 2014

Lo nuevo de Java 8: Interfaces funcionales

En Lo nuevo en Java 8 describimos brevemente las expresiones lambda en esta nueva versión de Java. Es necesario asociar a esas expresiones un tipo para de esta manera satisfacer la comprobación de tipos que el lenguaje utiliza. La interfaces funcionales cumplen ese objetivo, además de poder ser usadas como siempre.
Una interfaz funcional es una interfaz que contiene un único método abstracto, además de algunos métodos implementados. Estos últimos se conocen como métodos de default o métodos de extensión virtual. Las interfaces también pueden contener funciones estáticas, aunque esto último nada tiene que ver con el adjetivo funcional que se les da.
Una expresión lambda encapsula una funcionalidad específica y parece "normal" asignarle a ellas un tipo dado por una interfaz que declara la firma de esa función.
Ya estamos acostumbrados a utilizar una interfaz con un solo método para precisamente manipular la funcionalidad de ese método y poder reutilizarlo. El ejemplo más común es el de las interfaces que definen los "event handlers" en Swing y otros APIs.
Java 8 ha querido que ese idiom siga siendo usado para las expresiones lambda, y entonces bautiza a esa categoría de interfaces como interfaces funcionales.
Con propósitos informativos Java 8 define el tipo anotación correspondiente para indicar la intención de que el tipo interfaz anotada va a ser usado para describir una funcionalidad particular. Esa anotación está definida como:
@Documented
 @Retention(value=RUNTIME)
 @Target(value=TYPE)
public @interface FunctionalInterface
y las instancias de las interfaces funcionales podrán crearse con expresiones lambda, con referencias a métodos y con referencia a constructores. El compilador comprobará si una interfaz anotada con este tipo es realmente una interfaz funcional, aunque permitirá (como debe ser) utilizar cualquier interfaz con el propósito de interfaz funcional si ella es realmente una interfaz funcional aunque no esté anotada con esta anotación.
Como es conocido, existen en el lenguaje muchas interfaces funcionales. Algunas de ellas han estado presente desde la primera versión del lenguaje. java.lang.Runnable, por ejemplo, es una interfaz funcional que incluye sólo un método abstracto void run()

El paquete java.util.function

Este paquete incluye 40+ interfaces funcionales cuya intención es describir funcionalidades "comunes" que usamos en programación. Otros paquetes de "terceros" hacen lo mismo para funciones comunes en sus áreas.
Una de las interfaces funcionales en java.util.function es la interfaz java.util.function.Function. El código fuente simplificado de esta interfaz es:
package java.util.function;
@FunctionalInterface
public interface Function<extends Object, R extends Object> {
    public R apply(T t);
    //Métodos de default y funciones estáticas que por lo pronto no nos interesan
    }
}
Esta interfaz representa una función que recibe un argumento y produce un resultado. El método abstracto  public R apply(T t), una vez implementado será el usado cuando la expresión lambda sea invocada, siguiendo la sintaxis normal de invocación a funciones.
El ejemplo siguiente ilustra el uso de esta interfaz funcional. Se escriben dos métodos estáticos "generales" para ejecutar cualquier función trigonométrica en un caso y alguna operación que modifica una lista en el segundo. En el primer caso usamos lo nuevo de referencias a métodos para referirnos a algunas de las funciones que brinda la clase java.lang.Mathy en el segundo escribimos una expresión lambda muy sencilla "tipada" con esa interfaz funcional: inpt ->{Collections.sort(inpt);return inpt;};
class FunctionDemo {
   
     public static void main(String[] args) {
        // TODO code application logic here
        //Usamos un par de funciones trigonométricas, con referencias a métodos
        double result = FunctionDemo.trigonometric(Math.PI*30/180Math::sin);
        System.out.format("%s%f%s%f\n","sin(",Math.PI*30/180,") = ",result);
        result =FunctionDemo.trigonometric(Math.PI*30/180Math::cos);
        System.out.format("%s%f%s%f\n","cos(",Math.PI*30/180,") = ",result);
        //Usamos la función processingList con una expresión lambda
        Function<List<Integer>List<Integer>> f;
        f =inpt -> {Collections.sort(inpt);return inpt;};
        List<Integer> myList =Arrays.asList(2,4,5,6,18,25,131,4);
        FunctionDemo.processingCollections(myList,f);
        System.out.println(myList);
    }

  static double trigonometric(double value, Function<DoubleDouble> function){

    return function.apply(value);
 
  }
  static <T> List<T>processingCollections(List<T> value, Function<List<T>List<T>> function){
      return function.apply(value);
  }
}
La ejecución del programa anterior produce el siguiente resultado:
sin(0.523599) = 0.500000
cos(0.523599) = 0.866025
[124456131825]
and that´s all folks for today.

No comments: