2009-07-02 13 views
135

Esto puede ser algo común y trivial, pero parece que tengo problemas para encontrar una respuesta concreta. En C# hay un concepto de delegados, que se relaciona fuertemente con la idea de indicadores de función de C++. ¿Hay una funcionalidad similar en Java? Dado que los indicadores están algo ausentes, ¿cuál es la mejor manera de hacerlo? Y para ser claros, estamos hablando de primera clase aquí.Punteros de función en Java

+0

Tengo curiosidad por saber por qué querría esto donde los oyentes u otras construcciones OOP harían la misma tarea mientras conservaban su naturaleza de objeto. Quiero decir que entiendo la necesidad de la funcionalidad ofrecida por el concepto, solo que se puede lograr con un objeto simple ... a menos que, por supuesto, me esté faltando algo, ¡de ahí que haga esta pregunta! :-) – Newtopian

+1

Java 8 tiene expresiones lambda: http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html Es posible que desee comprobarlo. No es exactamente un puntero a la función, pero podría ser aún más útil. – FrustratedWithFormsDesigner

+5

Java 8 referencias de método es exactamente lo que está pidiendo. – Steven

Respuesta

113

El lenguaje Java para la funcionalidad de la función de puntero como es un una clase anónima implementar una interfaz, por ejemplo,

Collections.sort(list, new Comparator<MyClass>(){ 
    public int compare(MyClass a, MyClass b) 
    { 
     // compare objects 
    } 
}); 

Actualización: lo anterior es necesario en las versiones de Java antes de Java 8. Ahora tenemos alternativas mucho mejores, a saber: lambdas

list.sort((a, b) -> a.isGreaterThan(b)); 

y referencias de métodos:

list.sort(MyClass::isGreaterThan); 
+1

El patrón de diseño de estrategia. No es tan feo como un puntero de función C o C++ cuando desea que la función utilice datos no globales que no se proporcionan como argumento para la función. – Raedwald

+66

@Raedwald C++ tiene funtores, y C++ 0x tiene cierres y lambdas construidos sobre funtores. Incluso tiene 'std :: bind', que vincula los parámetros a las funciones y devuelve un objeto invocable. No puedo defender a C por estos motivos, pero C++ realmente es mejor que Java para esto. – zneak

+0

Bueno, C++ 0x podría ser, pero el estándar actual C++ no lo es. – Raedwald

20

No, las funciones no son objetos de primera clase en Java. Puede hacer lo mismo implementando una clase de controlador: así se implementan las devoluciones de llamada en el Swing, etc.

Sin embargo, hay propuestas de cierres (el nombre oficial de lo que está hablando) en futuras versiones de java - Javaworld tiene un artículo interesante.

+0

Interesante - gracias por la información. –

61

Puede sustituir un puntero de función con una interfaz. Supongamos que quiere ejecutar una colección y hacer algo con cada elemento.

public interface IFunction { 
    public void execute(Object o); 
} 

Ésta es la interfaz que podría pasar a algunos dicen CollectionUtils2.doFunc (Collection c, f iFunction).

public static void doFunc(Collection c, IFunction f) { 
    for (Object o : c) { 
     f.execute(o); 
    } 
} 

Como ejemplo digamos que tenemos una colección de números y desea agregar 1 a cada elemento.

CollectionUtils2.doFunc(List numbers, new IFunction() { 
    public void execute(Object o) { 
     Integer anInt = (Integer) o; 
     anInt++; 
    } 
}); 
+5

Es posible que desee agregar los genéricos a eso ... –

+4

Muy claro y conciso – dogeen

5

No existe tal cosa en Java. Tendrá que ajustar su función en algún objeto y pasar la referencia a ese objeto para pasar la referencia al método en ese objeto.

Sintácticamente, esto se puede aliviar en cierta medida mediante el uso de clases anónimas definidas en el lugar o clases anónimas definidas como variables miembro de la clase.

Ejemplo:

class MyComponent extends JPanel { 
    private JButton button; 
    public MyComponent() { 
     button = new JButton("click me"); 
     button.addActionListener(buttonAction); 
     add(button); 
    } 

    private ActionListener buttonAction = new ActionListener() { 
     public void actionPerformed(ActionEvent e) { 
      // handle the event... 
      // note how the handler instance can access 
      // members of the surrounding class 
      button.setText("you clicked me"); 
     } 
    } 
} 
6

Para lograr una funcionalidad similar se podría usar clases internas anónimas.

Si se va a definir una interfaz Foo:

interface Foo { 
    Object myFunc(Object arg); 
} 

crear un método bar que recibirá un 'puntero de función' como argumento:

public void bar(Foo foo) { 
    // ..... 
    Object object = foo.myFunc(argValue); 
    // ..... 
} 

Finalmente llamar al método de la siguiente manera:

bar(new Foo() { 
    public Object myFunc(Object arg) { 
     // Function code. 
    } 
} 
13

Esto le recuerda a Steve Yegge ' s Execution in the Kingdom of Nouns. Básicamente, establece que Java necesita un objeto para cada acción y, por lo tanto, no tiene entidades "solo verbales" como indicadores de función.

3

he implementado soporte de devolución de llamada/delegado en Java utilizando la reflexión. Los detalles y la fuente de trabajo son available on my website.

Cómo funciona

tenemos una clase llamada principio de devolución de llamada con una clase anidada nombrados WithParms. La API que necesita la devolución de llamada tomará un objeto Callback como parámetro y, si es necesario, creará una Callback.WithParms como variable de método. Como gran parte de las aplicaciones de este objeto serán recursivas, esto funciona de manera muy limpia.

Con el rendimiento sigue siendo una gran prioridad para mí, no quería que se me pidiera crear una matriz de objetos desechables para mantener los parámetros para cada invocación; después de todo, en una gran estructura de datos podría haber miles de elementos, y en un escenario de procesamiento de mensajes podríamos terminar procesando miles de estructuras de datos por segundo.

Para que sea seguro de enhebrar la matriz de parámetros debe existir únicamente para cada invocación del método de API, y para la eficiencia debe usarse la misma 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 pertenecía al objeto Callback. Además, la opción de invocación (que pasa los parámetros como una matriz o como objetos individuales) pertenece a la API mediante la devolución de llamada, lo que le permite usar 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 objeto de parámetro necesaria para las invocaciones de devolución de llamada, y proporciona 10 métodos invoke sobrecargados (con de 1 a 10 parámetros) que cargan el matriz de parámetros y luego invocar el objetivo de devolución de llamada.

40

Puede usar reflection para hacerlo.

Pase como parámetro el objeto y el nombre del método (como una cadena) y luego invoque el método.Por ejemplo:

Object methodCaller(Object theObject, String methodName) { 
    return theObject.getClass().getMethod(methodName).invoke(theObject); 
    // Catch the exceptions 
} 

y luego usarlo como en:

String theDescription = methodCaller(object1, "toString"); 
Class theClass = methodCaller(object2, "getClass"); 

Por supuesto, revise todas las excepciones y añadir los moldes necesarios.

+12

Esto es 1. feo y 2. lento. Pero sigue siendo una respuesta válida.;) – Makkes

+7

La reflexión no es la respuesta correcta a nada excepto "¿Cómo escribo el código Java lento lento": D – Jacob

+4

Puede ser "feo" y lento, pero creo que la reflexión permite hacer cosas que la solución basada en interfaz en la respuesta aceptada no puede funcionar (a menos que esté equivocado), es decir, llamar a varios métodos del mismo objeto dentro de la misma porción de código. – Eusebius

0

En relación con la mayoría de las personas aquí, soy nuevo en Java pero como no he visto una sugerencia similar, tengo otra alternativa que sugerir. No estoy seguro de si es una buena práctica o no, o incluso se sugirió antes y simplemente no lo entendí. Me gusta, ya que creo que es auto descriptivo.

/*Just to merge functions in a common name*/ 
public class CustomFunction{ 
public CustomFunction(){} 
} 

/*Actual functions*/ 
public class Function1 extends CustomFunction{ 
public Function1(){} 
public void execute(){...something here...} 
} 

public class Function2 extends CustomFunction{ 
public Function2(){} 
public void execute(){...something here...} 
} 

..... 
/*in Main class*/ 
CustomFunction functionpointer = null; 

continuación, dependiendo de la aplicación, asignar

functionpointer = new Function1(); 
functionpointer = new Function2(); 

etc.

y llamar por lambdas

functionpointer.execute(); 
5

Java8 ha introducido y method references. Por lo tanto, si su función coincide con functional interface (puede crear la suya propia), puede utilizar una referencia de método en este caso.

Java proporciona un conjunto de common functional interfaces. mientras que usted podría hacer lo siguiente:

public class Test { 
    public void test1(Integer i) {} 
    public void test2(Integer i) {} 
    public void consumer(Consumer<Integer> a) { 
    a.accept(10); 
    } 
    public void provideConsumer() { 
    consumer(this::test1); // method reference 
    consumer(x -> test2(x)); // lambda 
    } 
} 
Cuestiones relacionadas