¿Para qué se utilizan las interfaces funcionales en Java 8?


154

Encontré un nuevo término en Java 8: "interfaz funcional". Solo pude encontrar un uso mientras trabajaba con expresiones lambda .

Java 8 proporciona algunas interfaces funcionales integradas y, si queremos definir alguna interfaz funcional, podemos hacer uso de la @FunctionalInterfaceanotación. Nos permitirá declarar un solo método en la interfaz.

Por ejemplo:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

¿Qué tan útil es en Java 8 además de trabajar con expresiones lambda ?

(La pregunta aquí es diferente de la que hice. Pregunta por qué necesitamos interfaces funcionales mientras trabajamos con expresiones lambda. Mi pregunta es: ¿por qué otros usos tienen las interfaces funcionales además de las expresiones lambda?)


1
Se ve duplicado a este enlace. También hablan sobre por qué debería haber un solo método en la interfaz funcional. stackoverflow.com/questions/33010594/…
Kulbhushan Singh

1
@KulbhushanSingh Vi esta pregunta antes de publicar ... Ambas preguntas perciben la diferencia ...
Madhusudan

Respuestas:


127

@FunctionalInterfaceLa anotación es útil para verificar el tiempo de compilación de su código. No puede tener más de un método además static, defaulty métodos abstractos que anulan los métodos Objecten su @FunctionalInterfaceo cualquier otra interfaz utilizada como interfaz funcional.

Pero puede usar lambdas sin esta anotación, así como también puede anular métodos sin @Overrideanotación.

De documentos

Una interfaz funcional tiene exactamente un método abstracto. Como los métodos predeterminados tienen una implementación, no son abstractos. Si una interfaz declara un método abstracto que anula uno de los métodos públicos de java.lang.Object, eso tampoco cuenta para el recuento de métodos abstractos de la interfaz ya que cualquier implementación de la interfaz tendrá una implementación de java.lang.Object u otro lugar

Esto se puede usar en la expresión lambda:

public interface Foo {
  public void doSomething();
}

Esto no se puede usar en la expresión lambda:

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Pero esto dará error de compilación :

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Anotación inválida '@FunctionalInterface'; Foo no es una interfaz funcional


43
Para ser más precisos, debe tener exactamente un método abstracto que no anule un método java.lang.Objecten una interfaz funcional.
Holger

9
... y es ligeramente diferente a "no tener más de un publicmétodo además staticy default" ...
Holger

44
Todavía no entiendo ningún punto de tenerlo. ¿Por qué alguien en la tierra se molesta en verificar cuántos métodos tiene su interfaz? Las interfaces de marcador todavía tienen un punto y un propósito específico. La documentación y la respuesta solo explican lo que hace, no cómo es de ninguna utilidad. Y "usar" es exactamente lo que pidió el OP. Entonces no recomendaría esta respuesta.
saran3h

1
@VNT el error de compilación obtiene los clientes de esta interfaz, pero no la interfaz en sí puede cambiar. Con esta anotación, el error de compilación está en la interfaz, por lo que debe asegurarse de que nadie interrumpa a los clientes de su interfaz.
Sergii Bishyr

2
Esto muestra cómo usarlos, pero no explica por qué los necesitamos.
jeque

14

La documentación realmente hace una diferencia entre el propósito

Un tipo de anotación informativa que se utiliza para indicar que una declaración de tipo de interfaz está destinada a ser una interfaz funcional tal como se define en la Especificación del lenguaje Java.

y el caso de uso

Tenga en cuenta que las instancias de interfaces funcionales se pueden crear con expresiones lambda, referencias de métodos o referencias de constructor.

cuya redacción no excluye otros casos de uso en general. Dado que el objetivo principal es indicar una interfaz funcional , su pregunta real se reduce a "¿Hay otros casos de uso para interfaces funcionales que no sean expresiones lambda y referencias de método / constructor?"

Dado que la interfaz funcional es una construcción de lenguaje Java definida por la Especificación del lenguaje Java, solo esa especificación puede responder a esa pregunta:

JLS §9.8. Interfaces funcionales :

...

Además del proceso habitual de crear una instancia de interfaz declarando e instanciando una clase (§15.9), se pueden crear instancias de interfaces funcionales con expresiones de referencia de método y expresiones lambda (§15.13, §15.27).

Por lo tanto, la Especificación del lenguaje Java no dice lo contrario, el único caso de uso mencionado en esa sección es el de crear instancias de interfaz con expresiones de referencia de método y expresiones lambda. (Esto incluye referencias de constructor, ya que se indican como una forma de expresión de referencia de método en la especificación).

Entonces, en una oración, no, no hay otro caso de uso en Java 8.


Podría estar pidiendo demasiado o irrelevante (puede optar por no responder), pero ¿qué sugeriría cuando alguien creara una utilidad public static String generateTaskId()en lugar de hacerlo más "funcional"? Otra persona eligió escribirlo como public class TaskIdSupplier implements Supplier<String>con el getmétodo que usa implementación de generación existente. ¿Es eso un mal uso de las interfaces funcionales, especialmente la reutilización Supplierdel JDK incorporado? PD: No pude encontrar un lugar mejor / Preguntas y respuestas para preguntar esto. Feliz de migrar si pudiera sugerir.
Naman

1
@Naman no está haciendo que el método de utilidad sea más funcional cuando crea una clase con nombre TaskIdSupplier. Ahora, la pregunta es por qué creaste la clase nombrada. Hay escenarios en los que se necesita dicho tipo con nombre, por ejemplo, cuando desea apoyar la búsqueda de la implementación a través de ServiceLoader. No hay nada de malo en dejar que se implemente Supplierentonces. Pero cuando no lo necesite, no lo cree. Cuando solo necesita un Supplier<String>, ya es suficiente usar DeclaringClass::generateTaskIdy eliminar la necesidad de una clase explícita es el punto de esta característica del lenguaje.
Holger

Para ser honesto, estaba buscando una justificación para una recomendación que estaba transmitiendo. Por alguna razón en el trabajo, realmente no sentí que la TaskIdSupplierimplementación valiera la pena, pero luego el concepto de ServiceLoadertotalmente saltado de mi mente. Encontrado algunas preguntas durante estas discusiones que estaban teniendo como ¿Cuál es el uso de Supplier's publicexistencia cuando uno puede seguir adelante y desarrollar sus propias interfaces? y ¿Por qué no tener public static Supplier<String> TASK_ID_SUPPLIER = () ->...como una constante global? . (1/2)
Naman

1
@Naman, la forma idiomática de representar funciones en Java son métodos y evaluar esas funciones es idéntico a invocarlas. Nunca se debe obligar a un desarrollador a hacerlo en variable.genericMethodName(args)lugar de hacerlo meaningfulMethodName(args). El uso de un tipo de clase para representar una función, ya sea a través de la expresión / método de referencia lambda o una clase creada manualmente, es solo un vehículo para pasar la función (en ausencia de tipos de función verdaderos en Java). Esto solo debe hacerse cuando sea necesario.
Holger

1
Cuando tiene un pequeño fragmento de código que solo se pasa, puede crear una expresión lambda que lo encapsule. Siempre que sea necesario invocarlo como un método (esto incluye escenarios con una necesidad de prueba, cuando el fragmento de código no es trivial), cree un método con nombre que se pueda invocar y use una referencia de método o una expresión lambda / clase explícita encapsulando una llamada, para pasarla cuando sea necesario. Las constantes solo son útiles cuando no confía en la eficiencia de las expresiones lambda o referencias de métodos incrustadas en su código, en otras palabras, casi nunca son necesarias.
Holger

12

Como otros han dicho, una interfaz funcional es una interfaz que expone un método. Puede tener más de un método, pero todos los demás deben tener una implementación predeterminada. La razón por la que se llama "interfaz funcional" es porque efectivamente actúa como una función. Como puede pasar interfaces como parámetros, significa que las funciones ahora son "ciudadanos de primera clase" como en los lenguajes de programación funcionales. Esto tiene muchos beneficios, y los verá bastante cuando use la API Stream. Por supuesto, las expresiones lambda son el principal uso obvio para ellas.


10

De ningún modo. Las expresiones lambda son el único punto de esa anotación.


66
Bueno, los lamdbas también funcionan sin la anotación. Es una afirmación como @Overridedejarle saber al compilador que tenía la intención de escribir algo que fuera "funcional" (y obtener un error si se resbala).
Thilo

1
Directo al grano y la respuesta correcta, aunque un poco corto. Me tomé el tiempo para agregar una respuesta más elaborada diciendo lo mismo con más palabras ...
Holger

5

Se puede asignar una expresión lambda a un tipo de interfaz funcional, pero también se pueden hacer referencias a métodos y clases anónimas.

Lo bueno de las interfaces funcionales específicos en java.util.functiones que pueden estar compuestos para crear nuevas funciones (como Function.andThene Function.compose, Predicate.and, etc.) debido a los métodos predeterminados prácticos que contienen.


Deberías dar más detalles sobre este comentario. ¿Qué pasa con las referencias de métodos y las nuevas funciones?
K.Nicholas

5

Una interfaz con un solo método abstracto se llama Interfaz funcional. No es obligatorio usar @FunctionalInterface, pero es una buena práctica usarlo con interfaces funcionales para evitar la adición accidental de métodos adicionales. Si la interfaz se anota con la anotación @FunctionalInterface e intentamos tener más de un método abstracto, arroja un error del compilador.

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }

3

Interfaz funcional:

  • Introducido en Java 8
  • Interfaz que contiene un método de "resumen único".

Ejemplo 1:

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

Ejemplo 2

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

Ejemplo 3:

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Anotación Java8: @FunctionalInterface

  • La anotación comprueba que la interfaz contiene solo un método abstracto. De lo contrario, generar error.
  • A pesar de que falta @FunctionalInterface, sigue siendo una interfaz funcional (si tiene un método abstracto único). La anotación ayuda a evitar errores.
  • La interfaz funcional puede tener métodos estáticos y predeterminados adicionales.
  • por ejemplo, Iterable <>, Comparable <>, Comparator <>.

Aplicaciones de la interfaz funcional:

  • Referencias del método
  • Expresión lambda
  • Referencias del constructor

Para aprender interfaces funcionales, aprenda los primeros métodos predeterminados en la interfaz, y después de aprender la interfaz funcional, le resultará fácil comprender la referencia del método y la expresión lambda


¿Deberían sus dos primeros ejemplos tener una palabra clave "abstracta"?
sofs1

1
@ sofs1 Los métodos declarados en las interfaces son, por defecto, públicos y abstractos. Debe usar la palabra clave abstract en el caso de métodos en la clase abstract. Sin embargo, está bien usar palabras clave abstractas para métodos en la interfaz también. Lo han permitido para la compatibilidad de la versión anterior de Java, pero se desaconseja.
Ketan

2

Puedes usar lambda en Java 8

public static void main(String[] args) {
    tentimes(inputPrm - > System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
}

public static void tentimes(Consumer myFunction) {
    for (int i = 0; i < 10; i++)
        myFunction.accept("hello");
}

Para más información sobre Java Lambdas y FunctionalInterfaces


1

@FunctionalInterface Es una nueva anotación que se lanza con Java 8 y proporciona tipos de destino para expresiones lambda y se utiliza en la comprobación del tiempo de compilación de su código.

Cuando quieras usarlo:

1- Su interfaz no debe tener más de un método abstracto, de lo contrario se generará un error de compilación.

1- Su interfaz debe ser pura, lo que significa que la interfaz funcional está destinada a ser implementada por clases sin estado, por ejemplo, la interfaz de pure es Comparatorporque no depende del estado de los implementadores, en este caso no se dará ningún error de compilación, pero en muchos casos usted no podrá usar lambda con este tipo de interfaces

El java.util.functionpaquete contiene varias interfaces funcionales de propósito general, tales como Predicate, Consumer, Function, y Supplier.

También tenga en cuenta que puede usar lambdas sin esta anotación.


1

Además de otras respuestas, creo que la razón principal para "por qué usar una interfaz funcional que no sea directamente con expresiones lambda" puede estar relacionada con la naturaleza del lenguaje Java que está orientado a objetos.

Los principales atributos de las expresiones Lambda son: 1. Se pueden pasar alrededor de 2. y se pueden ejecutar en el futuro en un tiempo específico (varias veces). Ahora, para admitir esta función en idiomas, algunos otros idiomas se ocupan simplemente de este asunto.

Por ejemplo, en Java Script, una función (función anónima o literales de función) se puede abordar como un objeto. Por lo tanto, puede crearlos simplemente y también se pueden asignar a una variable y así sucesivamente. Por ejemplo:

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

o a través de ES6, puede usar una función de flecha.

const myFunction = ... => ...

Hasta ahora, los diseñadores de lenguaje Java no han aceptado manejar las características mencionadas de esta manera (técnicas de programación funcional). Creen que el lenguaje Java está orientado a objetos y, por lo tanto, deberían resolver este problema mediante técnicas orientadas a objetos. No quieren perderse la simplicidad y la coherencia del lenguaje Java.

Por lo tanto, usan interfaces, ya que cuando se necesita un objeto de una interfaz con un solo método (me refiero a la interfaz funcional), puede reemplazarlo con una expresión lambda. Como:

ActionListener listener = event -> ...;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.