style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Lo "nuevo" en 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<T 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.Math y 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/180, Math::sin);
        System.out.format("%s%f%s%f\n","sin(",Math.PI*30/180,") = ",result);
        result =FunctionDemo.trigonometric(Math.PI*30/180, Math::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,13, 1,4);
        FunctionDemo.processingCollections(myList,f);
        System.out.println(myList);
    }
 
  static double trigonometric(double value, Function<Double, Double> 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
[1, 2, 4, 4, 5, 6, 13, 18, 25]

and that´s all folks for today.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de bferro

Algo sobre interfaces funcionales

Espero no sea disfuncional

Interesante

Me gusta cómo va tomando el rumbo Java hacia algo más "funcional", pero dando pasos cortos, a diferencia de lenguajes cómo Scala o Clojure, en especial Clojure que son un tanto más radicales.

Lo interesante del asunto es que, tenemos algunas ventajas de lenguajes más orientados a lo funcional, pero fácil de asimilar, sin tener que cambiar (tanto) nuestra manera de pensar/programar.

Lo que si se echa de menos en comparación con otros lenguajes, y por otros lenguajes me refiero a C#, es la inferencia de tipos en general; algo cómo:
val x = "Hi"

Pero sin duda esto da para mucho. Oracle parece estar poniéndose las pilas.

Imagen de Cid

Interfaz funcional un solo metodo abstracto ¿Porque?

Quiero pensar que solo se define un solo metodo abstracto debido a que las interfaces funcionales estan pensadas para ser utilizadas en conjunto con una expresión lambda y no se si tengo bien o no el concepto pero dicha expresión se define como una función anónima, por tal motivo en tiempo de ejecución la JVM debe de asociar el cuerpo de la expresión lambda con el metodo abstracto que se definió en la interface funcional

@FunctionalInterface
public interface Ejemplo{

       public void echo(String msg);

}

public class Test{

      public static void main(String [] args){
            /*  Aqui la funcion anonima en tiempo de ejecución se asocia al único método que se definio en la interfaz */                
            Ejemplo e = (x) -> System.out.println(x);            
      }
}

Pero quiero entender porque no permitieron que esto se pueda hacer:

@FunctionalInterface
public interface Ejemplo{

       public void echo(String msg);
       public void defaultMethod();

}

public class Test{

      public static void main(String [] args){
            /*  Porque no poder elegir dentro de una gama de metodos abstractos en una interfaz ? */                
            Ejemplo e = (x) -> System.out.println(x);            
            Ejemplo e = () -> System.out.println("default");
      }
}

Imagen de julgo

default

default es una palabra reservada y debe ir antes de declarar el método, y al elegir que método abstracto usar se dejaría de implementar los métodos restantes

Imagen de Cid

Se me fue ese error

Ya corregí le puse defaultMethod(), @julgo gracias por corregir mi error, ok entonces si es por lo que comentaba si el segundo ejemplo fuera posible tendría que colocar las dos implementaciones si o si (debido a que son abstractas) y solo se tomaría en cuenta una, por lo tanto siempre estaríamos trabajando de más al implementar uno o más metodos que no usariamos.

Muchas gracias.

SAM interface

 

¡Psst! Oye, Hiroshige... Aunque estas diapositivas son de la era cuando caminaban los dinosaurios, seguramente las encontrarás muy interesantes: Closures in Java and Scala. ¡Espero ayude!

~~~

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">