À quoi servent les interfaces fonctionnelles dans Java 8?

154

Je suis tombé sur un nouveau terme en Java 8: "interface fonctionnelle". Je n'ai pu en trouver qu'une seule utilisation en travaillant avec des expressions lambda .

Java 8 fournit des interfaces fonctionnelles intégrées et si nous voulons définir une interface fonctionnelle, nous pouvons utiliser l' @FunctionalInterfaceannotation. Cela nous permettra de ne déclarer qu'une seule méthode dans l'interface.

Par exemple:

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

Dans quelle mesure est-il utile dans Java 8 autre que de simplement travailler avec des expressions lambda ?

(La question ici est différente de celle que j'ai posée. Elle demande pourquoi nous avons besoin d'interfaces fonctionnelles tout en travaillant avec des expressions lambda. Ma question est: pourquoi d'autres utilisations des interfaces fonctionnelles ont-elles en plus des expressions lambda?)

Madhusudan
la source
1
Il semble en double avec ce lien. Ils expliquent également pourquoi il ne devrait y avoir qu'une seule méthode dans Functional Interface. stackoverflow.com/questions/33010594/…
Kulbhushan Singh
1
@KulbhushanSingh J'ai vu cette question avant de poster ... Les deux questions sens la différence ...
Madhusudan

Réponses:

127

@FunctionalInterfaceL'annotation est utile pour vérifier le temps de compilation de votre code. Vous ne pouvez pas avoir plus d'une méthode en plus static, defaultet des méthodes abstraites qui remplacent des méthodes Objectdans votre @FunctionalInterfaceinterface ou toute autre interface utilisée comme interface fonctionnelle.

Mais vous pouvez utiliser des lambdas sans cette annotation ainsi que vous pouvez remplacer des méthodes sans @Overrideannotation.

À partir de documents

une interface fonctionnelle a exactement une méthode abstraite. Puisque les méthodes par défaut ont une implémentation, elles ne sont pas abstraites. Si une interface déclare une méthode abstraite remplaçant l'une des méthodes publiques de java.lang.Object, cela ne compte pas non plus dans le nombre de méthodes abstraites de l'interface car toute implémentation de l'interface aura une implémentation de java.lang.Object ou ailleurs

Cela peut être utilisé dans l'expression lambda:

public interface Foo {
  public void doSomething();
}

Cela ne peut pas être utilisé dans l'expression lambda:

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

Mais cela donnera une erreur de compilation :

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

Annotation '@FunctionalInterface' non valide; Foo n'est pas une interface fonctionnelle

Sergii Bishyr
la source
43
Pour être plus précis, vous devez avoir exactement une méthode abstraite qui ne remplace pas une méthode java.lang.Objectdans une interface fonctionnelle.
Holger
9
… Et c'est légèrement différent de «ne pas avoir plus d'une publicméthode en plus staticet default»…
Holger
4
Je ne comprends toujours pas l'intérêt de l'avoir. Pourquoi quelqu'un sur terre se soucierait-il de vérifier le nombre de méthodes de son interface? Les interfaces de marqueurs ont toujours un point et un but précis. La documentation et la réponse n'expliquent que ce qu'il fait, pas en quoi cela sert-il du tout. Et «utiliser» est exactement ce que le PO a demandé. Je ne recommanderais donc pas cette réponse.
saran3h
1
@VNT l'erreur de compilation récupère les clients de cette interface, mais pas l'interface elle-même peut changer. Avec cette annotation, l'erreur de compilation se trouve sur l'interface, vous vous assurez donc que personne ne cassera les clients de votre interface.
Sergii Bishyr
2
Cela montre comment les utiliser mais n'explique pas pourquoi nous en avons besoin.
sheikh
14

La documentation fait en effet une différence entre l'objectif

Un type d'annotation informatif utilisé pour indiquer qu'une déclaration de type d'interface est destinée à être une interface fonctionnelle telle que définie par la spécification du langage Java.

et le cas d'utilisation

Notez que les instances d'interfaces fonctionnelles peuvent être créées avec des expressions lambda, des références de méthode ou des références de constructeur.

dont la formulation n'exclut pas d'autres cas d'utilisation en général. Étant donné que l'objectif principal est d'indiquer une interface fonctionnelle , votre question réelle se résume à "Y a-t-il d'autres cas d'utilisation d' interfaces fonctionnelles autres que les expressions lambda et les références de méthode / constructeur?"

Étant donné que l'interface fonctionnelle est une construction de langage Java définie par la spécification du langage Java, seule cette spécification peut répondre à cette question:

JLS §9.8. Interfaces fonctionnelles :

En plus du processus habituel de création d'une instance d'interface en déclarant et en instanciant une classe (§15.9), des instances d'interfaces fonctionnelles peuvent être créées avec des expressions de référence de méthode et des expressions lambda (§15.13, §15.27).

Ainsi, la spécification du langage Java ne dit pas le contraire, le seul cas d'utilisation mentionné dans cette section est celui de la création d'instances d'interface avec des expressions de référence de méthode et des expressions lambda. (Cela inclut les références de constructeur car elles sont notées comme une forme d'expression de référence de méthode dans la spécification).

Donc, en une phrase, non, il n'y a pas d'autre cas d'utilisation pour Java 8.

Holger
la source
Peut-être simplement demander un peu trop ou pas pertinent (vous pouvez choisir de ne pas répondre), mais que suggéreriez-vous quand quelqu'un a créé un utilitaire public static String generateTaskId()par rapport à le rendre plus «fonctionnel», quelqu'un d'autre a choisi de l'écrire comme public class TaskIdSupplier implements Supplier<String>avec la getméthode utilisant le mise en œuvre de la génération existante. Est-ce une mauvaise utilisation des interfaces fonctionnelles, en particulier la réutilisation Supplierdu composant intégré de JDK? PS: Je n'ai pas pu trouver un meilleur endroit / Q & A pour demander cela. Heureux de migrer si vous le suggérez.
Naman le
1
@Naman vous ne rendez pas la méthode utilitaire plus fonctionnelle lorsque vous créez une classe nommée TaskIdSupplier. Maintenant, la question est de savoir pourquoi vous avez créé la classe nommée. Il existe des scénarios où un tel type nommé est nécessaire, par exemple lorsque vous souhaitez prendre en charge la recherche de l'implémentation via ServiceLoader. Il n'y a rien de mal à le laisser mettre en œuvre Supplieralors. Mais lorsque vous n'en avez pas besoin, ne le créez pas. Lorsque vous n'avez besoin que d'un Supplier<String>, il est déjà suffisant d'utiliser DeclaringClass::generateTaskIdet éliminer le besoin d'une classe explicite est le but de cette fonctionnalité de langage.
Holger le
Pour être honnête, je cherchais une justification pour une recommandation que je transmettais. Pour une raison quelconque au travail, je ne pensais pas vraiment que la TaskIdSuppliermise en œuvre en valait la peine, mais le concept de ServiceLoadertotalement sauté de mon esprit. Nous avons rencontré quelques questions lors de ces discussions que nous avions telles que A quoi sert l' existence de Supplier's publicquand on peut aller de l'avant et développer ses propres interfaces? et pourquoi ne pas avoir public static Supplier<String> TASK_ID_SUPPLIER = () ->...comme constante globale? . (1/2)
Naman le
1
@Naman la façon idiomatique de représenter les fonctions en Java sont des méthodes et l'évaluation de ces fonctions est identique à leur appel. Jamais un développeur ne devrait être obligé de faire à la variable.genericMethodName(args)place de meaningfulMethodName(args). L'utilisation d'un type de classe pour représenter une fonction, que ce soit via une référence d'expression / méthode lambda ou une classe créée manuellement, n'est qu'un moyen de transmettre la fonction (en l'absence de vrais types de fonction en Java). Cela ne devrait être fait qu'en cas de besoin.
Holger le
1
Lorsque vous avez un petit fragment de code uniquement transmis, vous pouvez créer une expression lambda l'encapsulant. Chaque fois qu'il est également nécessaire de l'invoquer comme une méthode (cela inclut des scénarios nécessitant des tests, lorsque le fragment de code n'est pas trivial), créez une méthode nommée qui peut être appelée et utilisez une référence de méthode ou une expression lambda / classe explicite encapsuler un appel, pour le transmettre en cas de besoin. Les constantes ne sont utiles que lorsque vous ne faites pas confiance à l'efficacité des expressions lambda ou des références de méthode intégrées dans votre code, en d'autres termes, elles ne sont presque jamais nécessaires.
Holger le
12

Comme d'autres l'ont dit, une interface fonctionnelle est une interface qui expose une méthode. Il peut avoir plusieurs méthodes, mais toutes les autres doivent avoir une implémentation par défaut. La raison pour laquelle on l'appelle "interface fonctionnelle" est qu'elle agit effectivement comme une fonction. Puisque vous pouvez passer des interfaces en tant que paramètres, cela signifie que les fonctions sont désormais des «citoyens de premier ordre» comme dans les langages de programmation fonctionnels. Cela présente de nombreux avantages, et vous les verrez beaucoup lorsque vous utilisez l'API Stream. Bien sûr, les expressions lambda en sont la principale utilisation évidente.

Sina Madani
la source
10

Pas du tout. Les expressions Lambda sont le seul et unique point de cette annotation.

Louis Wasserman
la source
6
Eh bien, les lamdbas fonctionnent également sans l'annotation. C'est une assertion comme @Overridepour faire savoir au compilateur que vous aviez l'intention d'écrire quelque chose qui était "fonctionnel" (et obtenir une erreur si vous avez glissé).
Thilo
1
Droit au but et la bonne réponse, bien qu'un peu courte. J'ai pris le temps d'ajouter une réponse plus élaborée en disant la même chose avec plus de mots…
Holger
5

Une expression lambda peut être affectée à un type d'interface fonctionnelle, mais il en est de même pour les références de méthode et les classes anonymes.

Une bonne chose au sujet des interfaces fonctionnelles spécifiques java.util.functionest qu'ils peuvent être composés pour créer de nouvelles fonctions (comme Function.andThenet Function.compose, Predicate.and, etc.) en raison des méthodes par défaut pratiques qu'ils contiennent.

Hank D
la source
Vous devriez développer davantage ce commentaire. Qu'en est-il des références de méthodes et des nouvelles fonctions?
K.Nicholas
5

Une interface avec une seule méthode abstraite est appelée Interface fonctionnelle. Il n'est pas obligatoire d'utiliser @FunctionalInterface, mais il est recommandé de l'utiliser avec des interfaces fonctionnelles pour éviter l'ajout accidentel de méthodes supplémentaires. Si l'interface est annotée avec l'annotation @FunctionalInterface et que nous essayons d'avoir plus d'une méthode abstraite, cela génère une erreur du compilateur.

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;
        }
    }
Akhilesh
la source
3

Interface fonctionnelle:

  • Introduit dans Java 8
  • Interface qui contient une méthode "abstraite unique".

Exemple 1:

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

Exemple 2:

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

Exemple 3:

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

Annotation Java8 - @FunctionalInterface

  • Annotation vérifiez que l'interface ne contient qu'une seule méthode abstraite. Sinon, signalez l'erreur.
  • Même si @FunctionalInterface manque, il s'agit toujours d'une interface fonctionnelle (si une seule méthode abstraite). L'annotation permet d'éviter les erreurs.
  • L'interface fonctionnelle peut avoir des méthodes statiques et par défaut supplémentaires.
  • Par exemple, Iterable <>, Comparable <>, Comparator <>.

Applications de l'interface fonctionnelle:

  • Références de méthode
  • Expression Lambda
  • Références constructeurs

Pour apprendre les interfaces fonctionnelles, apprendre les premières méthodes par défaut dans l'interface, et après avoir appris l'interface fonctionnelle, il vous sera facile de comprendre la référence de méthode et l'expression lambda

Ketan
la source
Vos deux premiers exemples devraient-ils avoir un mot-clé «abstrait»?
sofs1
1
@ sofs1 Les méthodes déclarées dans les interfaces sont par défaut à la fois publiques et abstraites. Vous devez utiliser un mot-clé abstrait dans le cas de méthodes en classe abstraite. Cependant, il est également possible d'utiliser des mots-clés abstraits pour les méthodes dans l'interface. Ils l'ont autorisé pour la compatibilité de l'ancienne version de Java, mais il est déconseillé.
Ketan
2

Vous pouvez utiliser lambda dans 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");
}

Pour plus d'informations sur Java Lambdas et FunctionalInterfaces

Websterix
la source
1

@FunctionalInterface est une nouvelle annotation est publiée avec Java 8 et fournit des types de cible pour les expressions lambda et elle est utilisée lors de la vérification de la compilation de votre code.

Lorsque vous souhaitez l'utiliser:

1- Votre interface ne doit pas avoir plus d'une méthode abstraite, sinon une erreur de compilation sera donnée.

1- Votre interface doit être pure, ce qui signifie que l'interface fonctionnelle est destinée à être implémentée par des classes sans état, exmple de pure est Comparatorinterface car elle ne dépend pas de l'état de l'implémenteur, dans ce cas, aucune erreur de compilation ne sera donnée, mais dans de nombreux cas, vous ne pourra pas utiliser lambda avec ce genre d'interfaces

Le java.util.functionpaquet contient diverses interfaces fonctionnelles à usage général tels que Predicate, Consumer, Function, et Supplier.

Veuillez également noter que vous pouvez utiliser des lambdas sans cette annotation.

Ahmad Al-Kurdi
la source
1

À côté d'autres réponses, je pense que la raison principale pour "pourquoi utiliser l'interface fonctionnelle autrement qu'avec des expressions lambda" peut être liée à la nature du langage Java qui est orienté objet.

Les principaux attributs des expressions Lambda sont: 1. Ils peuvent être transmis 2. et ils peuvent être exécutés à l'avenir à un moment précis (plusieurs fois). Maintenant, pour prendre en charge cette fonctionnalité dans les langues, d'autres langues traitent simplement de cette question.

Par exemple, dans Java Script, une fonction (fonction anonyme ou littéraux de fonction) peut être adressée en tant qu'objet. Ainsi, vous pouvez les créer simplement et ils peuvent également être affectés à une variable et ainsi de suite. Par exemple:

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

ou via ES6, vous pouvez utiliser une fonction fléchée.

const myFunction = ... => ...

Jusqu'à présent, les concepteurs de langage Java n'ont pas accepté de gérer les fonctionnalités mentionnées de cette manière (techniques de programmation fonctionnelle). Ils croient que le langage Java est orienté objet et doivent donc résoudre ce problème via des techniques orientées objet. Ils ne veulent pas manquer la simplicité et la cohérence du langage Java.

Par conséquent, ils utilisent des interfaces, car lorsqu'un objet d'une interface avec une seule méthode (je veux dire une interface fonctionnelle) est nécessaire, vous pouvez le remplacer par une expression lambda. Tel que:

ActionListener listener = event -> ...;
MMKarami
la source