Je suis passé à Java à partir de C # après quelques recommandations de la part de CodeReview. Donc, quand je regardais LWJGL, une chose dont je me souvenais était que chaque appel à Display
devait être exécuté sur le même thread que la Display.create()
méthode était invoquée. En me souvenant de cela, j'ai concocté un cours qui ressemble un peu à ça.
public class LwjglDisplayWindow implements DisplayWindow {
private final static int TargetFramesPerSecond = 60;
private final Scheduler _scheduler;
public LwjglDisplayWindow(Scheduler displayScheduler, DisplayMode displayMode) throws LWJGLException {
_scheduler = displayScheduler;
Display.setDisplayMode(displayMode);
Display.create();
}
public void dispose() {
Display.destroy();
}
@Override
public int getTargetFramesPerSecond() { return TargetFramesPerSecond; }
@Override
public Future<Boolean> isClosed() {
return _scheduler.schedule(() -> Display.isCloseRequested());
}
}
En écrivant cette classe, vous remarquerez que j'ai créé une méthode appelée isClosed()
qui renvoie a Future<Boolean>
. Cela envoie une fonction à mon Scheduler
interface (qui n'est rien de plus qu'un wrapper autour d'un ScheduledExecutorService
. Lors de l'écriture de la schedule
méthode sur le Scheduler
j'ai remarqué que je pouvais soit utiliser un Supplier<T>
argument ou un Callable<T>
argument pour représenter la fonction transmise. ScheduledExecutorService
Ne contenait pas de remplacer pour Supplier<T>
mais j'ai remarqué que l'expression lambda () -> Display.isCloseRequested()
est en fait compatible avec les deux types Callable<bool>
et Supplier<bool>
.
Ma question est, y a-t-il une différence entre ces deux, sémantiquement ou autrement - et si oui, qu'est-ce que c'est, pour que je puisse y adhérer?
Réponses:
La réponse courte est que les deux utilisent des interfaces fonctionnelles, mais il convient également de noter que toutes les interfaces fonctionnelles ne doivent pas avoir l'
@FunctionalInterface
annotation. La partie critique de JavaDoc se lit comme suit:Et la définition la plus simple d'une interface fonctionnelle est (simplement, sans autres exclusions) juste:
Par conséquent, dans la réponse de @Maciej Chalapuk , il est également possible de supprimer l'annotation et de spécifier le lambda souhaité:
Maintenant, ce qui fait les deux
Callable
etSupplier
les interfaces fonctionnelles, c'est qu'elles contiennent exactement une méthode abstraite:Callable.call()
Supplier.get()
Puisque les deux méthodes n'acceptent pas d'argument (contrairement à l'
MyInterface.myCall(int)
exemple), les paramètres formels sont empty (()
).Comme vous devriez pouvoir en déduire maintenant, c'est simplement parce que les deux méthodes abstraites renverront le type de l'expression que vous utilisez. Vous devriez certainement utiliser un
Callable
donné votre utilisation d'unScheduledExecutorService
.Poursuite de l'exploration (au-delà de la portée de la question)
Les deux interfaces proviennent de packages entièrement différents , ils sont donc également utilisés différemment. Dans votre cas, je ne vois pas comment une implémentation de sera utilisée, sauf si elle fournit :
Supplier<T>
Callable
Le premier
() ->
peut être interprété librement comme "unSupplier
donne ..." et le second comme "unCallable
donne ...".return value;
est le corps de laCallable
lambda, qui est lui-même le corps de laSupplier
lambda.Cependant, l' utilisation dans cet exemple artificiel devient légèrement compliqué, que vous avez besoin maintenant
get()
de laSupplier
première avantget()
Opération permettant le résultat de laFuture
, qui à son tourcall()
votreCallable
manière asynchrone.la source
Une différence fondamentale entre les 2 interfaces est que Callable permet de lever les exceptions vérifiées depuis son implémentation, contrairement au fournisseur.
Voici les extraits de code du JDK soulignant cela -
la source
Supplier
, comme l'API de flux. Vous pouvez absolument transmettre des lambdas et des références de méthode à des méthodes qui prennent unCallable
.Comme vous le constatez, en pratique, ils font la même chose (fournissent une sorte de valeur), mais en principe, ils sont destinés à faire des choses différentes:
A
Callable
est " Une tâche qui renvoie un résultat , tandis que aSupplier
est" un fournisseur de résultats ". En d'autres termes, aCallable
est un moyen de référencer une unité de travail non encore exécutée, tandis que aSupplier
est un moyen de référencer une valeur encore inconnue.Il est possible que a
Callable
fasse très peu de travail et renvoie simplement une valeur. Il est également possible que l'onSupplier
puisse faire beaucoup de travail (par exemple, construire une grande structure de données). Mais de manière générale, ce qui vous intéresse, c'est leur objectif principal. Par exemple, unExecutorService
fonctionne avecCallable
s, car son objectif principal est d' exécuter des unités de travail. Un magasin de données chargé paresseux utiliserait unSupplier
, parce qu'il prend soin d'être fourni une valeur, sans trop se soucier de la façon dont beaucoup de travail qui pourrait prendre.Une autre façon de formuler la distinction est que a
Callable
peut avoir des effets secondaires (par exemple, écrire dans un fichier), tandis que aSupplier
devrait généralement être exempt d'effets secondaires. La documentation ne le mentionne pas explicitement (car ce n'est pas une exigence ), mais je suggère de penser en ces termes. Si l'œuvre est idempotente, utilisez aSupplier
, sinon utilisez aCallable
.la source
Les deux sont des interfaces Java normales sans sémantique particulière. Callable fait partie de l'API simultanée. Le fournisseur fait partie de la nouvelle API de programmation fonctionnelle. Ils peuvent être créés à partir d'expressions lambda grâce aux modifications de Java8. @FunctionalInterface fait vérifier au compilateur que l'interface est fonctionnelle et déclenche une erreur si ce n'est pas le cas, mais une interface n'a pas besoin de cette annotation pour être une interface fonctionnelle et être implémentée par lambdas. Cela ressemble à la façon dont une méthode peut être un remplacement sans être marquée @Override mais pas vice-versa.
Vous pouvez définir vos propres interfaces compatibles avec lambdas et les documenter avec des
@FunctionalInterface
annotations. La documentation est cependant facultative.la source
IntPredicate
en Java.