Ponteiros de Função em Java

Isso pode ser algo comum e trivial, mas parece que estou tendo problemas para encontrar uma resposta concreta. Em C # há um conceito de delegates, que se relaciona fortemente com a idéia de pointers de function do C ++. Existe uma funcionalidade semelhante em Java? Dado que os pointers estão um pouco ausentes, qual é a melhor maneira disso? E para ser claro, estamos falando de primeira class aqui.

O idioma Java para a funcionalidade function-pointer-like é uma class anônima que implementa uma interface, por exemplo

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

Atualização: o acima é necessário em versões Java anteriores ao Java 8. Agora temos alternativas muito mais agradáveis, ou seja, lambdas:

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

e referências de método:

 list.sort(MyClass::isGreaterThan); 

Você pode replace um ponteiro de function por uma interface. Vamos dizer que você deseja executar uma coleção e fazer algo com cada elemento.

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

Esta é a interface que poderíamos passar para alguns, por exemplo, CollectionUtils2.doFunc (Coleção c, IFunção f).

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

Por exemplo, digamos que temos uma coleção de números e você gostaria de adicionar 1 a cada elemento.

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

Você pode usar a reflection para fazer isso.

Passe como parâmetro o object e o nome do método (como uma string) e invoque o método. Por exemplo:

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

E então use-o como em:

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

Claro, verifique todas as exceções e adicione os castings necessários.

Não, funções não são objects de primeira class em java. Você pode fazer a mesma coisa implementando uma class de manipulador – é assim que as chamadas de retorno são implementadas no Swing etc.

Existem no entanto propostas para encerramentos (o nome oficial para o que você está falando) em futuras versões do java – o Javaworld tem um artigo interessante.

Isto traz à mente a Execução de Steve Yegge no Reino dos Substantivos . Basicamente, ele afirma que Java precisa de um object para cada ação e, portanto, não possui entidades “somente verbais”, como os pointers de function.

Para obter uma funcionalidade semelhante, você poderia usar classs internas anônimas.

Se você fosse definir uma interface Foo :

 interface Foo { Object myFunc(Object arg); } 

Crie uma bar método que receberá um ‘ponteiro de function’ como um argumento:

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

Por fim, chame o método da seguinte maneira:

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

Não existe tal coisa em Java. Você precisará envolver sua function em algum object e passar a referência a esse object para passar a referência ao método naquele object.

Sintaticamente, isso pode ser facilitado até certo ponto usando classs anônimas definidas no local ou classs anônimas definidas como variables ​​de membro da class.

Exemplo:

 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"); } } } 

O Java8 introduziu lambdas e referências de methods . Portanto, se sua function corresponder a uma interface funcional (você pode criar sua própria), você pode usar uma referência de método nesse caso.

Java fornece um conjunto de interfaces funcionais comuns . Considerando que você poderia fazer o seguinte:

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

Eu implementei callback / delegate suporte em Java usando reflection. Detalhes e fonte de trabalho estão disponíveis no meu site .

Como funciona

Temos uma class principal chamada Callback com uma class aninhada chamada WithParms. A API que precisa do retorno de chamada levará um object de retorno de chamada como um parâmetro e, se necessário, criará uma variável Callback.WithParms como método. Como muitas das aplicações deste object serão recursivas, isso funciona de maneira muito clara.

Com o desempenho ainda sendo uma prioridade alta para mim, eu não queria ser obrigado a criar uma matriz de objects descartáveis ​​para manter os parâmetros para cada chamada – afinal, em uma estrutura de dados grande, poderia haver milhares de elementos e em um processamento de mensagens cenário, poderíamos acabar processando milhares de estruturas de dados por segundo.

Para ser threadsafe, a matriz de parâmetros precisa existir exclusivamente para cada chamada do método da API e, para fins de eficiência, a mesma deve ser usada para cada chamada do retorno de chamada; Eu precisava de um segundo object que seria barato para criar, a fim de ligar o retorno de chamada com uma matriz de parâmetros para invocação. Mas, em alguns cenários, o invocador já teria uma matriz de parâmetros por outros motivos. Por esses dois motivos, a matriz de parâmetros não pertencia ao object de retorno de chamada. Além disso, a escolha da invocação (passando os parâmetros como uma matriz ou como objects individuais) pertence às mãos da API usando o retorno de chamada, permitindo-lhe usar qualquer chamada que seja mais adequada ao seu funcionamento interno.

A class aninhada WithParms, portanto, é opcional e serve a dois propósitos, ela contém a matriz de object de parâmetro necessária para as chamadas de retorno de chamada e fornece 10 methods invoke () com (de 1 a 10 parâmetros) que carregam a matriz de parâmetros e invocar o destino de retorno de chamada.

Verifique os fechamentos como eles foram implementados na biblioteca de lambdaj. Eles realmente têm um comportamento muito semelhante aos delegates do C #:

http://code.google.com/p/lambdaj/wiki/Closures

Em relação à maioria das pessoas aqui eu sou novo em java, mas desde que eu não vi uma sugestão semelhante, eu tenho outra alternativa para sugerir. Não tenho certeza se é uma boa prática ou não, ou mesmo sugerido antes e eu simplesmente não entendi. Eu apenas gosto desde que eu acho que é auto descritivo.

  /*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; 

dependendo do aplicativo, atribua

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

etc.

e chamar por

  functionpointer.execute();