@Suma ¿Cómo puede ser un duplicado si la pregunta que mencionaste fue publicada un año después de esta?
Ani
1
Java 8 tiene una característica bastante parecida a los delegados. Se llama lambdas.
tbodt
44
@tbodt para ser más correcto, Java 8 tiene una característica bastante similar a los delegados. Se llama interfaces funcionales . Las lambdas son una forma de crear tales instancias de delegado (anónimamente).
@nawfal, +1, pero para ser aún más correcto, Java 7 y versiones anteriores ya tienen una característica similar a los delegados. Se llama interfaces simples.
Pacerier
Respuestas:
152
No, realmente no.
Es posible que pueda lograr el mismo efecto utilizando la reflexión para obtener los objetos del Método que luego puede invocar, y la otra forma es crear una interfaz con un solo método 'invocar' o 'ejecutar', y luego instanciarlos para llamar al método le interesa (es decir, usar una clase interna anónima).
Con clases internas anónimas, incluso puede evitar declarar clases con nombre separadas y casi tratarlas como funciones de delegado reales.
// JavapublicvoidSomeMethod(ISomeBehaviour pSomeBehaviour){...}...SomeMethod(newISomeBehaviour(){@OverridepublicvoidSomeFunction(){// your implementation}});
Esto probablemente solo debería usarse cuando la implementación es muy específica para el contexto actual y no se beneficiaría de su reutilización.
Y luego, por supuesto, en Java 8, se convierten básicamente en expresiones lambda:
// Java 8SomeMethod(()->{/* your implementation */});
+1. esta es una solución decente, y la verbosidad puede compensarse con mantenibilidad en el futuro.
nawfal
esto es genial ... Actualmente estoy trabajando en un proyecto donde no puedo usar la reflexión debido a las limitaciones del proyecto y esta solución hace el trabajo maravillosamente :)
Jonathan Camarena
¿Java tiene una convención de nomenclatura de prefijo I para las interfaces? No he visto eso antes
La versión más reciente del entorno de desarrollo Microsoft Visual J ++ admite una construcción de lenguaje llamada delegados o referencias de métodos enlazados . Esta construcción, y las nuevas palabras clave delegatee
multicastintroducidas para admitirla, no forman parte del
lenguaje de programación Java TM , que está especificado por la Especificación del lenguaje Java y modificado por la Especificación de clases internas incluida en la documentación para el software JDKTM 1.1 .
Es poco probable que el lenguaje de programación Java incluya alguna vez esta construcción. Sun ya consideró cuidadosamente adoptarlo en 1996, hasta el punto de construir y descartar prototipos funcionales. Nuestra conclusión fue que las referencias de métodos vinculados son innecesarias y perjudiciales para el lenguaje. Esta decisión se tomó en consulta con Borland International, que tenía experiencia previa con referencias de métodos vinculados en Delphi Object Pascal.
Creemos que las referencias de métodos vinculados son innecesarias porque otra alternativa de diseño, las clases internas , proporciona una funcionalidad igual o superior. En particular, las clases internas son totalmente compatibles con los requisitos del manejo de eventos de la interfaz de usuario y se han utilizado para implementar una API de interfaz de usuario al menos tan completa como las clases de Windows Foundation.
Creemos que las referencias de métodos vinculados son perjudiciales porque restan valor a la simplicidad del lenguaje de programación Java y al carácter generalizado orientado a objetos de las API. Las referencias de métodos enlazados también introducen irregularidades en la sintaxis del lenguaje y las reglas de alcance. Finalmente, diluyen la inversión en tecnologías VM porque se requiere que las VM manejen tipos de referencias adicionales y enlaces de métodos diferentes de manera eficiente.
Como dice en qué Patrick se vinculó , desea usar clases internas en su lugar.
SCdF
16
Excelente artículo. ME ENCANTA su definición de "simple": Java está diseñado para ser un lenguaje simple como en "Java tiene que ser simple para compilar / VM para" mientras ignora "Java tiene que ser simple para escribir / leer por una persona" . Explica mucho
Juozas Kontvainis
55
Solo creo que SUN cometió un gran error. Simplemente no han sido convencidos por el paradigma funcional, y eso es todo.
Stephane Rolland
77
@Juozas: Python se hace explícitamente para que sea simple de escribir / leer por una persona y sí implementa funciones / delegados lambda .
Stephane Rolland
55
Reemplazado con un enlace archive.org. Además, eso es realmente estúpido, Oracle.
Los delegados son una construcción útil en sistemas basados en eventos. Esencialmente, los Delegados son objetos que codifican un envío de método en un objeto especificado. Este documento muestra cómo las clases internas de Java proporcionan una solución más genérica a tales problemas.
¿Qué es un delegado? Realmente es muy similar a un puntero a la función miembro como se usa en C ++. Pero un delegado contiene el objeto de destino junto con el método a invocar. Idealmente, sería bueno poder decir:
obj.registerHandler (ano.methodOne);
..y que el método methodOne se invocaría en ano cuando se recibiera algún evento específico.
Esto es lo que logra la estructura de delegado.
Clases internas de Java
Se ha argumentado que Java proporciona esta funcionalidad a través de clases internas anónimas y, por lo tanto, no necesita la construcción Delegado adicional.
A primera vista, esto parece correcto pero al mismo tiempo una molestia. Porque para muchos ejemplos de procesamiento de eventos, la simplicidad de la sintaxis de Delegados es muy atractiva.
Manejador general
Sin embargo, si la programación basada en eventos se usa de manera más generalizada, por ejemplo, como parte de un entorno general de programación asincrónica, hay más en juego.
En una situación tan general, no es suficiente incluir solo el método de destino y la instancia del objeto de destino. En general, puede haber otros parámetros necesarios, que se determinan dentro del contexto cuando se registra el controlador de eventos.
En esta situación más general, el enfoque de Java puede proporcionar una solución muy elegante, particularmente cuando se combina con el uso de variables finales:
Tenga en cuenta que las variables finales son accesibles desde las definiciones de método de clase anónima. Asegúrese de estudiar este código cuidadosamente para comprender las ramificaciones. Esta es potencialmente una técnica muy poderosa. Por ejemplo, se puede utilizar con buenos resultados al registrar controladores en MiniDOM y en situaciones más generales.
Por el contrario, el constructo Delegado no proporciona una solución para este requisito más general y, como tal, debe rechazarse como un idioma en el que se pueden basar los diseños.
Sé que esta publicación es antigua, pero Java 8 ha agregado lambdas y el concepto de una interfaz funcional, que es cualquier interfaz con un solo método. Juntos, ofrecen una funcionalidad similar a los delegados de C #. Consulte aquí para obtener más información, o simplemente google Java Lambdas.
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
Lo bueno de este idioma es que puede verificar que el método delegado a exista y que tenga la firma requerida, en el punto donde crea el delegador (aunque no en tiempo de compilación, desafortunadamente, aunque un complemento FindBugs podría ayuda aquí), luego úselo de manera segura para delegar en varias instancias.
He implementado el soporte de devolución de llamada / delegado en Java usando la reflexión. Los detalles y la fuente de trabajo están disponibles en mi sitio web .
Cómo funciona
Hay una clase principal llamada Callback con una clase anidada llamada WithParms. La API que necesita la devolución de llamada tomará un objeto de devolución de llamada como parámetro y, si es necesario, creará un Callback.WithParms como una variable de método. Dado que muchas de las aplicaciones de este objeto serán recursivas, esto funciona de manera muy limpia.
Dado que el rendimiento sigue siendo una gran prioridad para mí, no quería que se me pidiera que creara una matriz de objetos desechables para contener los parámetros para cada invocación; después de todo, en una estructura de datos de gran tamaño podría haber miles de elementos y un procesamiento de mensajes escenario podríamos terminar procesando miles de estructuras de datos por segundo.
Para ser seguro para subprocesos, la matriz de parámetros debe existir de manera única para cada invocación del método API, y para mayor eficacia, se debe usar el mismo para cada invocación de la devolución de llamada; Necesitaba un segundo objeto que sería barato de crear para vincular la devolución de llamada con una matriz de parámetros para la invocación. Pero, en algunos escenarios, el invocador ya tendría una matriz de parámetros por otros motivos. Por estos dos motivos, la matriz de parámetros no pertenece al objeto Callback. Además, la elección de la invocación (pasando los parámetros como una matriz o como objetos individuales) pertenece a la API utilizando la devolución de llamada que le permite utilizar la invocación que mejor se adapte a su funcionamiento interno.
La clase anidada WithParms, entonces, es opcional y tiene dos propósitos: contiene la matriz de objetos de parámetros necesarios para las invocaciones de devolución de llamada, y proporciona 10 métodos invoke () sobrecargados (con 1 a 10 parámetros) que cargan la matriz de parámetros y luego invocar el objetivo de devolución de llamada.
Lo que sigue es un ejemplo usando una devolución de llamada para procesar los archivos en un árbol de directorios. Este es un pase de validación inicial que solo cuenta los archivos a procesar y garantiza que ninguno exceda un tamaño máximo predeterminado. En este caso, simplemente creamos la devolución de llamada en línea con la invocación de la API. Sin embargo, reflejamos el método de destino como un valor estático para que la reflexión no se realice siempre.
staticprivatefinalMethod COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);...IoUtil.processDirectory(root,newCallback(this,COUNT),selector);...privatevoid callback_count(File dir,File fil){if(fil!=null){// file is null for processing a directory
fileTotal++;if(fil.length()>fileSizeLimit){thrownewAbort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);}}
progress("Counting",dir,fileTotal);}
IoUtil.processDirectory ():
/**
* Process a directory using callbacks. To interrupt, the callback must throw an (unchecked) exception.
* Subdirectories are processed only if the selector is null or selects the directories, and are done
* after the files in any given directory. When the callback is invoked for a directory, the file
* argument is null;
* <p>
* The callback signature is:
* <pre> void callback(File dir, File ent);</pre>
* <p>
* @return The number of files processed.
*/staticpublicint processDirectory(File dir,Callback cbk,FileSelector sel){return _processDirectory(dir,newCallback.WithParms(cbk,2),sel);}staticprivateint _processDirectory(File dir,Callback.WithParms cbk,FileSelector sel){int cnt=0;if(!dir.isDirectory()){if(sel==null|| sel.accept(dir)){ cbk.invoke(dir.getParent(),dir); cnt++;}}else{
cbk.invoke(dir,(Object[])null);File[] lst=(sel==null? dir.listFiles(): dir.listFiles(sel));if(lst!=null){for(int xa=0; xa<lst.length; xa++){File ent=lst[xa];if(!ent.isDirectory()){
cbk.invoke(dir,ent);
lst[xa]=null;
cnt++;}}for(int xa=0; xa<lst.length; xa++){File ent=lst[xa];if(ent!=null){ cnt+=_processDirectory(ent,cbk,sel);}}}}return cnt;}
Este ejemplo ilustra la belleza de este enfoque: la lógica específica de la aplicación se abstrae en la devolución de llamada, y el trabajo pesado de recorrer recursivamente un árbol de directorios está bien escondido en un método de utilidad estática completamente reutilizable. Y no tenemos que pagar repetidamente el precio de definir e implementar una interfaz para cada nuevo uso. Por supuesto, el argumento para una interfaz es que es mucho más explícito sobre qué implementar (se aplica, no simplemente se documenta), pero en la práctica no he encontrado que sea un problema obtener la definición correcta de devolución de llamada.
Definir e implementar una interfaz no es realmente tan malo (a menos que esté distribuyendo applets, como yo lo hago, donde evitar crear clases adicionales realmente importa), pero donde esto realmente brilla es cuando tiene múltiples devoluciones de llamada en una sola clase. No solo se está forzando a empujarlos a cada uno en una clase interna separada agregada sobrecarga en la aplicación implementada, sino que es francamente tedioso programar y todo ese código de placa de caldera es realmente solo "ruido".
Sí y no, pero el patrón delegado en Java podría pensarse de esta manera. Este video tutorial trata sobre el intercambio de datos entre fragmentos de actividad, y tiene una gran esencia de delegar un patrón mediante interfaces.
No tiene una delegatepalabra clave explícita como C #, pero puede lograr algo similar en Java 8 utilizando una interfaz funcional (es decir, cualquier interfaz con exactamente un método) y lambda:
privateinterfaceSingleFunc{void printMe();}publicstaticvoid main(String[] args){SingleFunc sf =()->{System.out.println("Hello, I am a simple single func.");};SingleFunc sfComplex =()->{System.out.println("Hello, I am a COMPLEX single func.");};
delegate(sf);
delegate(sfComplex);}privatestaticvoid delegate(SingleFunc f){
f.printMe();}
Cada nuevo objeto de tipo SingleFuncdebe implementarse printMe(), por lo que es seguro pasarlo a otro método (por ejemplo delegate(SingleFunc)) para llamar al printMe()método.
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
StackFlowed
2
@StackFlowed Elimina el enlace y ¿qué obtienes? Una publicación con información. Vota para mantener.
Scimonster
1
@Scimonster, ¿qué pasaría si el enlace ya no es válido?
StackFlowed
@StackFlowed Todavía proporciona una respuesta: use un Proxy Java.
Scimonster
entonces podría dejarse como un comentario?
StackFlowed
0
No, pero tiene un comportamiento similar, internamente.
En C #, los delegados se utilizan para crear un punto de entrada separado y funcionan de manera muy similar a un puntero de función.
En java no hay nada como puntero de función (en una vista superior) pero internamente Java necesita hacer lo mismo para lograr estos objetivos.
Por ejemplo, la creación de subprocesos en Java requiere una clase que amplíe el subproceso o implemente Runnable, porque una variable de objeto de clase se puede usar como un puntero de ubicación de memoria.
No, Java no tiene esa característica sorprendente. Pero podría crearlo manualmente utilizando el patrón de observador. Aquí hay un ejemplo:
escriba delegado de C # en Java
El código descrito ofrece muchas de las ventajas de los delegados de C #. Los métodos, ya sea estáticos o dinámicos, pueden tratarse de manera uniforme. La complejidad en los métodos de llamada a través de la reflexión se reduce y el código es reutilizable, en el sentido de que no requiere clases adicionales en el código del usuario. Tenga en cuenta que estamos llamando a una versión alternativa de invoke, donde se puede llamar a un método con un parámetro sin crear una matriz de objetos. Código Java a continuación:
Java no tiene delegados y está orgulloso de ello :). Por lo que leí aquí, encontré en esencia 2 formas de falsificar delegados: 1. reflexión; 2. clase interna
¡Las reflexiones son deslumbrantes! La clase interna no cubre el caso de uso más simple: la función de clasificación. No quiero entrar en detalles, pero la solución con la clase interna básicamente es crear una clase de contenedor para una matriz de enteros que se ordenarán en orden ascendente y una clase para una matriz de enteros que se ordenarán en orden descendente.
Respuestas:
No, realmente no.
Es posible que pueda lograr el mismo efecto utilizando la reflexión para obtener los objetos del Método que luego puede invocar, y la otra forma es crear una interfaz con un solo método 'invocar' o 'ejecutar', y luego instanciarlos para llamar al método le interesa (es decir, usar una clase interna anónima).
También puede encontrar este artículo interesante / útil: un programador de Java mira a los delegados de C # (@ archive.org)
fuente
Dependiendo exactamente de lo que quiere decir, puede lograr un efecto similar (pasar un método) usando el Patrón de estrategia.
En lugar de una línea como esta que declara una firma de método con nombre:
declarar una interfaz:
Para implementaciones concretas del método, defina una clase que implemente el comportamiento:
Luego, donde haya tenido un
SomeFunction
delegado en C #, use unaISomeBehaviour
referencia en su lugar:Con clases internas anónimas, incluso puede evitar declarar clases con nombre separadas y casi tratarlas como funciones de delegado reales.
Esto probablemente solo debería usarse cuando la implementación es muy específica para el contexto actual y no se beneficiaría de su reutilización.
Y luego, por supuesto, en Java 8, se convierten básicamente en expresiones lambda:
fuente
Cuento corto: no .
fuente
¿Has leído esto ?
fuente
Sé que esta publicación es antigua, pero Java 8 ha agregado lambdas y el concepto de una interfaz funcional, que es cualquier interfaz con un solo método. Juntos, ofrecen una funcionalidad similar a los delegados de C #. Consulte aquí para obtener más información, o simplemente google Java Lambdas. http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
fuente
No, pero son falsificables usando proxies y reflejos:
Lo bueno de este idioma es que puede verificar que el método delegado a exista y que tenga la firma requerida, en el punto donde crea el delegador (aunque no en tiempo de compilación, desafortunadamente, aunque un complemento FindBugs podría ayuda aquí), luego úselo de manera segura para delegar en varias instancias.
Vea el código karg en github para más pruebas e implementación .
fuente
He implementado el soporte de devolución de llamada / delegado en Java usando la reflexión. Los detalles y la fuente de trabajo están disponibles en mi sitio web .
Cómo funciona
Hay una clase principal llamada Callback con una clase anidada llamada WithParms. La API que necesita la devolución de llamada tomará un objeto de devolución de llamada como parámetro y, si es necesario, creará un Callback.WithParms como una variable de método. Dado que muchas de las aplicaciones de este objeto serán recursivas, esto funciona de manera muy limpia.
Dado que el rendimiento sigue siendo una gran prioridad para mí, no quería que se me pidiera que creara una matriz de objetos desechables para contener los parámetros para cada invocación; después de todo, en una estructura de datos de gran tamaño podría haber miles de elementos y un procesamiento de mensajes escenario podríamos terminar procesando miles de estructuras de datos por segundo.
Para ser seguro para subprocesos, la matriz de parámetros debe existir de manera única para cada invocación del método API, y para mayor eficacia, se debe usar el mismo para cada invocación de la devolución de llamada; Necesitaba un segundo objeto que sería barato de crear para vincular la devolución de llamada con una matriz de parámetros para la invocación. Pero, en algunos escenarios, el invocador ya tendría una matriz de parámetros por otros motivos. Por estos dos motivos, la matriz de parámetros no pertenece al objeto Callback. Además, la elección de la invocación (pasando los parámetros como una matriz o como objetos individuales) pertenece a la API utilizando la devolución de llamada que le permite utilizar la invocación que mejor se adapte a su funcionamiento interno.
La clase anidada WithParms, entonces, es opcional y tiene dos propósitos: contiene la matriz de objetos de parámetros necesarios para las invocaciones de devolución de llamada, y proporciona 10 métodos invoke () sobrecargados (con 1 a 10 parámetros) que cargan la matriz de parámetros y luego invocar el objetivo de devolución de llamada.
Lo que sigue es un ejemplo usando una devolución de llamada para procesar los archivos en un árbol de directorios. Este es un pase de validación inicial que solo cuenta los archivos a procesar y garantiza que ninguno exceda un tamaño máximo predeterminado. En este caso, simplemente creamos la devolución de llamada en línea con la invocación de la API. Sin embargo, reflejamos el método de destino como un valor estático para que la reflexión no se realice siempre.
IoUtil.processDirectory ():
Este ejemplo ilustra la belleza de este enfoque: la lógica específica de la aplicación se abstrae en la devolución de llamada, y el trabajo pesado de recorrer recursivamente un árbol de directorios está bien escondido en un método de utilidad estática completamente reutilizable. Y no tenemos que pagar repetidamente el precio de definir e implementar una interfaz para cada nuevo uso. Por supuesto, el argumento para una interfaz es que es mucho más explícito sobre qué implementar (se aplica, no simplemente se documenta), pero en la práctica no he encontrado que sea un problema obtener la definición correcta de devolución de llamada.
Definir e implementar una interfaz no es realmente tan malo (a menos que esté distribuyendo applets, como yo lo hago, donde evitar crear clases adicionales realmente importa), pero donde esto realmente brilla es cuando tiene múltiples devoluciones de llamada en una sola clase. No solo se está forzando a empujarlos a cada uno en una clase interna separada agregada sobrecarga en la aplicación implementada, sino que es francamente tedioso programar y todo ese código de placa de caldera es realmente solo "ruido".
fuente
Sí y no, pero el patrón delegado en Java podría pensarse de esta manera. Este video tutorial trata sobre el intercambio de datos entre fragmentos de actividad, y tiene una gran esencia de delegar un patrón mediante interfaces.
fuente
No tiene una
delegate
palabra clave explícita como C #, pero puede lograr algo similar en Java 8 utilizando una interfaz funcional (es decir, cualquier interfaz con exactamente un método) y lambda:Cada nuevo objeto de tipo
SingleFunc
debe implementarseprintMe()
, por lo que es seguro pasarlo a otro método (por ejemplodelegate(SingleFunc)
) para llamar alprintMe()
método.fuente
Si bien no es tan limpio, podría implementar algo como delegados de C # usando un Proxy Java .
fuente
No, pero tiene un comportamiento similar, internamente.
En C #, los delegados se utilizan para crear un punto de entrada separado y funcionan de manera muy similar a un puntero de función.
En java no hay nada como puntero de función (en una vista superior) pero internamente Java necesita hacer lo mismo para lograr estos objetivos.
Por ejemplo, la creación de subprocesos en Java requiere una clase que amplíe el subproceso o implemente Runnable, porque una variable de objeto de clase se puede usar como un puntero de ubicación de memoria.
fuente
No, Java no tiene esa característica sorprendente. Pero podría crearlo manualmente utilizando el patrón de observador. Aquí hay un ejemplo: escriba delegado de C # en Java
fuente
El código descrito ofrece muchas de las ventajas de los delegados de C #. Los métodos, ya sea estáticos o dinámicos, pueden tratarse de manera uniforme. La complejidad en los métodos de llamada a través de la reflexión se reduce y el código es reutilizable, en el sentido de que no requiere clases adicionales en el código del usuario. Tenga en cuenta que estamos llamando a una versión alternativa de invoke, donde se puede llamar a un método con un parámetro sin crear una matriz de objetos. Código Java a continuación:
fuente
Java no tiene delegados y está orgulloso de ello :). Por lo que leí aquí, encontré en esencia 2 formas de falsificar delegados: 1. reflexión; 2. clase interna
¡Las reflexiones son deslumbrantes! La clase interna no cubre el caso de uso más simple: la función de clasificación. No quiero entrar en detalles, pero la solución con la clase interna básicamente es crear una clase de contenedor para una matriz de enteros que se ordenarán en orden ascendente y una clase para una matriz de enteros que se ordenarán en orden descendente.
fuente