He estado cambiando a Java desde C # después de algunas recomendaciones de algunos en CodeReview. Entonces, cuando estaba buscando en LWJGL, una cosa que recordaba era que cada llamada a Displaydebe ejecutarse en el mismo hilo en el que Display.create()se invocó el método. Recordando esto, preparé una clase que se parece un poco a esto.
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());
}
}
Mientras escribe esta clase, notará que creé un método llamado isClosed()que devuelve a Future<Boolean>. Esto envía una función a mi Schedulerinterfaz (que no es más que un contenedor alrededor de un ScheduledExecutorService. Al escribir el schedulemétodo en el Schedulernoté que podía usar un Supplier<T>argumento o un Callable<T>argumento para representar la función que se pasa. ScheduledExecutorServiceNo contenía un anule Supplier<T>pero, noté que la expresión lambda () -> Display.isCloseRequested()es de tipo compatible con ambos Callable<bool> y Supplier<bool> .
Mi pregunta es, ¿hay alguna diferencia entre esos dos, semánticamente o de otro modo? Y, de ser así, ¿cuál es, para que pueda adherirme?

Respuestas:
La respuesta corta es que ambos están utilizando interfaces funcionales, pero también vale la pena señalar que no todas las interfaces funcionales deben tener la
@FunctionalInterfaceanotación. La parte crítica de JavaDoc dice:Y la definición más simple de una interfaz funcional es (simplemente, sin otras exclusiones) solo:
Por lo tanto, en la respuesta de @Maciej Chalapuk , también es posible eliminar la anotación y especificar la lambda deseada:
Ahora, lo que hace que ambos
CallableySupplierfuncionales de interfaces se debe a que contienen exactamente un método abstracto:Callable.call()Supplier.get()Como ambos métodos no toman en cuenta un argumento (a diferencia del
MyInterface.myCall(int)ejemplo), los parámetros formales están vacíos (()).Como ya debería poder inferir, eso se debe a que ambos métodos abstractos devolverán el tipo de expresión que usa. Definitivamente deberías estar usando un
Callabledado tu uso de aScheduledExecutorService.Exploración adicional (más allá del alcance de la pregunta)
Ambas interfaces provienen de paquetes completamente diferentes , por lo tanto, también se usan de manera diferente. En su caso, no veo cómo se utilizará una implementación de , a menos que proporcione un :
Supplier<T>CallableEl primero
() ->se puede interpretar libremente como "unSupplierda ..." y el segundo como "unCallableda ...".return value;es el cuerpo de laCallablelambda, que es el cuerpo de laSupplierlambda.Sin embargo, el uso en este ejemplo artificial se vuelve un poco complicado, ya que ahora debe hacerlo
get()desde elSupplierprimero antes deget()obtener su resultado desde elFuture, que acall()su vez seráCallableasincrónico.fuente
Una diferencia básica entre las 2 interfaces es que Callable permite que se lancen excepciones controladas desde su implementación, mientras que el Proveedor no.
Aquí están los fragmentos de código del JDK que destacan esto:
fuente
Supplier, como la API de transmisión. Absolutamente puede pasar lambdas y referencias de métodos a métodos que toman unCallable.Como observa, en la práctica hacen lo mismo (proporcionan algún tipo de valor), sin embargo, en principio, tienen la intención de hacer cosas diferentes:
A
Callablees " Una tarea que devuelve un resultado , mientras que aSupplieres" un proveedor de resultados ". En otras palabras, aCallablees una forma de hacer referencia a una unidad de trabajo aún no ejecutada, mientras que aSupplieres una forma de hacer referencia a un valor aún desconocido.Es posible que a
Callablepueda hacer muy poco trabajo y simplemente devolver un valor. También es posible queSupplierpueda hacer mucho trabajo (por ejemplo, construir una estructura de datos grande). Pero, en general, lo que te importa es su propósito principal. Por ejemplo, unExecutorServicefunciona conCallables, porque su propósito principal es ejecutar unidades de trabajo. Un almacén de datos con carga lenta usaría aSupplier, porque le importa que se le proporcione un valor, sin preocuparse por la cantidad de trabajo que podría tomar.Otra forma de expresar la distinción es que a
Callablepuede tener efectos secundarios (por ejemplo, escribir en un archivo), mientrasSupplierque generalmente no debería tener efectos secundarios. La documentación no menciona explícitamente esto (ya que no es un requisito ), pero sugeriría pensar en esos términos. Si el trabajo es idempotente, use aSupplier, si no, use aCallable.fuente
Ambas son interfaces Java normales sin semántica especial. Callable es parte de la API concurrente. El proveedor es parte de la nueva API de programación funcional. Se pueden crear a partir de expresiones lambda gracias a los cambios en Java8. @FunctionalInterface hace que el compilador compruebe que la interfaz es funcional y genera un error si no lo es, pero una interfaz no necesita esa anotación para ser una interfaz funcional e implementada por lambdas. Así es como un método puede ser una anulación sin estar marcado @Override pero no al revés.
Puede definir sus propias interfaces compatibles con lambdas y documentarlas con
@FunctionalInterfaceanotaciones. Sin embargo, la documentación es opcional.fuente
IntPredicateen Java.