A cosa servono le interfacce funzionali in Java 8?


154

Mi sono imbattuto in un nuovo termine in Java 8: "interfaccia funzionale". Ne ho trovato solo un uso mentre lavoravo con le espressioni lambda .

Java 8 fornisce alcune interfacce funzionali integrate e se vogliamo definire qualsiasi interfaccia funzionale, possiamo usare l' @FunctionalInterfaceannotazione. Ci permetterà di dichiarare solo un singolo metodo nell'interfaccia.

Per esempio:

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

Quanto è utile in Java 8 oltre a lavorare con le espressioni lambda ?

(La domanda qui è diversa da quella che ho posto. Si sta chiedendo perché abbiamo bisogno di interfacce funzionali mentre lavoriamo con le espressioni lambda. La mia domanda è: perché altri usi hanno interfacce funzionali oltre alle espressioni lambda?)


1
Sembra duplcate a questo link. Parlano anche del perché ci dovrebbe essere un solo metodo nell'interfaccia funzionale. stackoverflow.com/questions/33010594/…
Kulbhushan Singh

1
@KulbhushanSingh Ho visto questa domanda prima di pubblicare ... Entrambe le domande rilevano la differenza ...
Madhusudan,

Risposte:


127

@FunctionalInterfacel'annotazione è utile per il controllo del tempo di compilazione del codice. Non si può avere più di un metodo, oltre static, defaulte metodi astratti che i metodi di override in Objectnel vostro @FunctionalInterfaceo di qualsiasi altra interfaccia usata come interfaccia funzionale.

Ma puoi usare lambdas senza questa annotazione e puoi ignorare i metodi senza @Overrideannotazione.

Da documenti

un'interfaccia funzionale ha esattamente un metodo astratto. Poiché i metodi predefiniti hanno un'implementazione, non sono astratti. Se un'interfaccia dichiara un metodo astratto che sovrascrive uno dei metodi pubblici di java.lang.Object, anche questo non conta per il conteggio dei metodi astratti dell'interfaccia poiché qualsiasi implementazione dell'interfaccia avrà un'implementazione da java.lang.Object o altrove

Questo può essere usato nell'espressione lambda:

public interface Foo {
  public void doSomething();
}

Questo non può essere usato nell'espressione lambda:

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

Ma questo darà errore di compilazione :

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

Annotazione "@FunctionalInterface" non valida; Foo non è un'interfaccia funzionale


43
Per essere più precisi, devi avere esattamente un metodo astratto che non sostituisce un metodo in java.lang.Objectun'interfaccia funzionale.
Holger,

9
... ed è leggermente diverso dal "non avere più di un publicmetodo oltre statice default" ...
Holger,

4
Ancora non capisco il punto di averlo. Perché qualcuno sulla terra dovrebbe preoccuparsi di controllare quanti metodi ha la sua interfaccia. Le interfacce dei marker hanno ancora un punto e uno scopo specifico. La documentazione e la risposta spiegano solo cosa fa, non come è del tutto utile. E "usare" è esattamente ciò che l'OP ha chiesto. Quindi non consiglierei questa risposta.
saran3h,

1
@VNT l'errore di compilazione ottiene i client di questa interfaccia, ma non può cambiare l'interfaccia stessa. Con questa annotazione l'errore di compilazione si trova nell'interfaccia, quindi ti assicuri che nessuno rompa i client della tua interfaccia.
Sergii Bishyr,

2
Questo mostra come usarli ma non spiega perché ne abbiamo bisogno.
sceicco,

14

La documentazione fa davvero la differenza tra lo scopo

Un tipo di annotazione informativo utilizzato per indicare che una dichiarazione del tipo di interfaccia deve essere un'interfaccia funzionale come definita dalle specifiche del linguaggio Java.

e il caso d'uso

Si noti che è possibile creare istanze di interfacce funzionali con espressioni lambda, riferimenti a metodi o riferimenti a costruttori.

la cui formulazione non preclude altri casi d'uso in generale. Poiché lo scopo principale è quello di indicare un'interfaccia funzionale , la tua vera domanda si riduce a "Esistono altri casi d'uso per interfacce funzionali diverse dalle espressioni lambda e dai riferimenti a metodo / costruttore?"

Poiché l' interfaccia funzionale è un costrutto del linguaggio Java definito dalla specifica del linguaggio Java, solo quella specifica può rispondere a questa domanda:

JLS §9.8. Interfacce funzionali :

...

Oltre al normale processo di creazione di un'istanza di interfaccia dichiarando e istanziando una classe (§15.9), è possibile creare istanze di interfacce funzionali con espressioni di riferimento del metodo ed espressioni lambda (§15.13, §15.27).

Quindi la specifica del linguaggio Java non dice diversamente, l'unico caso d'uso menzionato in quella sezione è quello di creare istanze di interfaccia con espressioni di riferimento del metodo ed espressioni lambda. (Ciò include i riferimenti del costruttore in quanto sono indicati come una forma di espressione di riferimento del metodo nella specifica).

Quindi in una frase, no, non c'è altro caso d'uso in Java 8.


Potrebbe essere solo chiedere un po 'troppo o irrilevante (puoi scegliere di non rispondere), ma cosa suggeriresti quando qualcuno ha creato un'utilità public static String generateTaskId()rispetto a renderla più "funzionale" qualcun altro ha scelto di scriverlo come public class TaskIdSupplier implements Supplier<String>con il getmetodo usando il metodo implementazione della generazione esistente. È un uso improprio delle interfacce funzionali, in particolare il riutilizzo Supplierdel JDK integrato? PS: Non sono riuscito a trovare un posto migliore / Domande e risposte per chiederlo. Felice di migrare se potessi suggerire.
Naman

1
@Naman non stai rendendo il metodo di utilità più funzionale quando crei una classe con nome TaskIdSupplier. Ora, la domanda è: perché hai creato la classe nominata. Esistono scenari in cui è necessario un tale tipo di nome, ad esempio quando si desidera supportare la ricerca dell'implementazione tramite ServiceLoader. Non c'è nulla di sbagliato nel lasciarlo implementare Supplierallora. Ma quando non ne hai bisogno, non crearlo. Quando hai solo bisogno di un Supplier<String>, è già sufficiente usare DeclaringClass::generateTaskIded eliminare la necessità di una classe esplicita è il punto di questa funzionalità linguistica.
Holger

Ad essere sincero, cercavo una giustificazione per una raccomandazione che stavo trasmettendo. Per qualche motivo al lavoro non pensavo davvero che l' TaskIdSupplierimplementazione valesse la pena, ma poi il concetto di mi ha ServiceLoadercompletamente ignorato. Incontrato alcune domande durante queste discussioni eravamo avendo come ad esempio cosa è l'uso di Supplier's publicesistenza, quando si può andare avanti e sviluppare le proprie interfacce? e perché non avere public static Supplier<String> TASK_ID_SUPPLIER = () ->...come costante globale? . (1/2)
Naman

1
@Naman il modo idiomatico di rappresentare le funzioni in Java sono metodi e la valutazione di tali funzioni è identica a invocarle. Uno sviluppatore non dovrebbe mai essere costretto a fare variable.genericMethodName(args)invece di meaningfulMethodName(args). L'uso di un tipo di classe per rappresentare una funzione, sia tramite espressione lambda / riferimento al metodo o una classe creata manualmente, è solo un veicolo per passare la funzione (in assenza di veri tipi di funzione in Java). Questo dovrebbe essere fatto solo quando necessario.
Holger

1
Quando hai solo un piccolo frammento di codice che viene passato in giro, puoi creare un'espressione lambda che lo incapsula. Ogni volta che c'è anche la necessità di invocarlo come un metodo (questo include scenari con necessità di test, quando il frammento di codice non è banale), creare un metodo denominato che può essere invocato e utilizzare un riferimento al metodo o un'espressione lambda / classe esplicita incapsulare una chiamata, per passarla quando necessario. Le costanti sono utili solo quando non ti fidi dell'efficienza delle espressioni lambda o dei riferimenti ai metodi incorporati nel tuo codice, in altre parole, non sono quasi mai necessarie.
Holger

12

Come altri hanno già detto, un'interfaccia funzionale è un'interfaccia che espone un metodo. Può avere più di un metodo, ma tutti gli altri devono avere un'implementazione predefinita. La ragione per cui è chiamata "interfaccia funzionale" è perché agisce efficacemente come una funzione. Poiché è possibile passare le interfacce come parametri, significa che le funzioni sono ora "cittadini di prima classe" come nei linguaggi di programmazione funzionale. Questo ha molti vantaggi e li vedrai parecchio quando usi l'API Stream. Naturalmente, le espressioni lambda sono il principale uso ovvio per loro.


10

Affatto. Le espressioni lambda sono il solo e unico punto di quell'annotazione.


6
Bene, lamdbas funziona anche senza l'annotazione. È un'affermazione proprio come @Overridefar sapere al compilatore che intendevi scrivere qualcosa di "funzionale" (e ottenere un errore se sei scivolato).
Thilo,

1
Dritto al punto e la risposta corretta, anche se un po 'breve. Mi sono preso il tempo di aggiungere una risposta più elaborata dicendo la stessa cosa con più parole ...
Holger

5

Un'espressione lambda può essere assegnata a un tipo di interfaccia funzionale, ma anche riferimenti a metodi e classi anonime.

Una cosa bella di specifiche interfacce funzionali java.util.functionè che possono essere composti per creare nuove funzioni (come Function.andThene Function.compose, Predicate.ande così via) a causa dei metodi predefiniti a portata di mano che essi contengono.


Dovresti approfondire questo commento di più. Che dire di riferimenti a metodi e nuove funzioni?
K. Nicholas

5

Un'interfaccia con un solo metodo astratto è chiamata Interfaccia funzionale. Non è obbligatorio utilizzare @FunctionalInterface, ma è consigliabile utilizzarlo con interfacce funzionali per evitare l'aggiunta accidentale di metodi aggiuntivi. Se l'interfaccia è annotata con l'annotazione @FunctionalInterface e proviamo ad avere più di un metodo astratto, genera un errore del compilatore.

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

Interfaccia funzionale:

  • Introdotto in Java 8
  • Interfaccia che contiene un metodo "single abstract".

Esempio 1:

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

Esempio 2:

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

Esempio 3:

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

Annotazione Java8 - @FunctionalInterface

  • Verificare che l'interfaccia contenga solo un metodo astratto. In caso contrario, genera un errore.
  • Anche se manca @FunctionalInterface, è comunque un'interfaccia funzionale (se ha un unico metodo astratto). L'annotazione aiuta a evitare errori.
  • L'interfaccia funzionale può avere metodi statici e predefiniti aggiuntivi.
  • ad es. Iterable <>, Comparable <>, Comparator <>.

Applicazioni dell'interfaccia funzionale:

  • Riferimenti a metodi
  • Espressione lambda
  • Riferimenti del costruttore

Per apprendere le interfacce funzionali, apprendere i primi metodi predefiniti nell'interfaccia e dopo aver appreso l'interfaccia funzionale, sarà facile comprendere il riferimento al metodo e l'espressione lambda


I tuoi primi due esempi dovrebbero avere una parola chiave "astratta"?
sofs1,

1
@ sofs1 I metodi dichiarati nelle interfacce sono di default sia pubblici che astratti. Devi usare una parola chiave astratta in caso di metodi in classe astratta. Comunque va bene usare parole chiave astratte anche per metodi nell'interfaccia. Lo hanno permesso per la compatibilità della versione precedente di Java ma è sconsigliato.
Ketan,

2

Puoi usare lambda in 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");
}

Per ulteriori informazioni su Java Lambdas e FunctionalInterfaces


1

@FunctionalInterface è una nuova annotazione rilasciata con Java 8 e fornisce tipi di destinazione per le espressioni lambda e viene utilizzata per il controllo del tempo di compilazione del codice.

Quando vuoi usarlo:

1- La tua interfaccia non deve avere più di un metodo astratto, altrimenti verrà fornito un errore di compilazione.

1- La tua interfaccia dovrebbe essere pura, il che significa che l'interfaccia funzionale deve essere implementata da classi senza stato, l'esempio di pure è Comparatorinterfaccia perché non dipende dallo stato degli implementatori, in questo caso No verrà fornito errore di compilazione, ma in molti casi non sarà in grado di usare lambda con questo tipo di interfacce

Il java.util.functionpacchetto contiene varie interfacce funzionali general purpose quali Predicate, Consumer, FunctioneSupplier .

Si noti inoltre che è possibile utilizzare lambdas senza questa annotazione.


1

Accanto ad altre risposte, penso che la ragione principale per "perché usare l'interfaccia funzionale diversa direttamente dalle espressioni lambda" possa essere correlata alla natura del linguaggio Java che è orientato agli oggetti.

Gli attributi principali delle espressioni Lambda sono: 1. Possono essere passati intorno a 2. e possono essere eseguiti in futuro in tempi specifici (più volte). Ora per supportare questa funzione nelle lingue, alcune altre lingue affrontano semplicemente questa questione.

Ad esempio in Java Script, una funzione (funzione anonima o valori letterali di funzione) può essere indirizzata come oggetto. Quindi, puoi crearli semplicemente e anche loro possono essere assegnati a una variabile e così via. Per esempio:

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

o tramite ES6, è possibile utilizzare una funzione freccia.

const myFunction = ... => ...

Fino ad ora, i progettisti del linguaggio Java non hanno accettato di gestire le funzionalità menzionate in questo modo (tecniche di programmazione funzionale). Credono che il linguaggio Java sia orientato agli oggetti e quindi dovrebbero risolvere questo problema tramite tecniche orientate agli oggetti. Non vogliono perdere la semplicità e la coerenza del linguaggio Java.

Pertanto, usano le interfacce, come quando è necessario un oggetto di un'interfaccia con un solo metodo (intendo l'interfaccia funzionale) è possibile sostituirlo con un'espressione lambda. Ad esempio:

ActionListener listener = event -> ...;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.