Método Java Pass como parâmetro

Eu estou procurando uma maneira de passar um método por referência. Eu entendo que Java não passa methods como parâmetros, no entanto, eu gostaria de obter uma alternativa.

Já me disseram que as interfaces são a alternativa para passar methods como parâmetros, mas não entendo como uma interface pode atuar como um método por referência. Se bem entendi uma interface é simplesmente um conjunto abstrato de methods que não estão definidos. Eu não quero enviar uma interface que precisa ser definida toda vez porque vários methods diferentes poderiam chamar o mesmo método com os mesmos parâmetros.

O que eu gostaria de realizar é algo semelhante a isto:

public void setAllComponents(Component[] myComponentArray, Method myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { //recursive call if Container Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } //end if node myMethod(leaf); } //end looping through components } 

chamado como:

 setAllComponents(this.getComponents(), changeColor()); setAllComponents(this.getComponents(), changeSize()); 

Editar : a partir do Java 8, expressões lambda são uma boa solução, como outras respostas apontaram. A resposta abaixo foi escrita para o Java 7 e versões anteriores …


Dê uma olhada no padrão de comando .

 // NOTE: code not tested, but I believe this is valid java... public class CommandExample { public interface Command { public void execute(Object data); } public class PrintCommand implements Command { public void execute(Object data) { System.out.println(data.toString()); } } public static void callCommand(Command command, Object data) { command.execute(data); } public static void main(String... args) { callCommand(new PrintCommand(), "hello world"); } } 

Edit: como Pete Kirkham aponta , há outra maneira de fazer isso usando um visitante . A abordagem do visitante é um pouco mais complexa – todos os nós precisam estar cientes do visitante com um método acceptVisitor() – mas se você precisar percorrer um gráfico de object mais complexo, então vale a pena examinar.

No Java 8, agora você pode passar um método mais facilmente usando Expressões Lambda . Primeiro, alguns antecedentes. Uma interface funcional é uma interface que possui um e apenas um método abstrato, embora possa conter qualquer número de methods padrão (novos no Java 8) e methods estáticos. Uma expressão lambda pode implementar rapidamente o método abstrato, sem toda a syntax desnecessária necessária se você não usar uma expressão lambda.

Sem expressões lambda:

 obj.aMethod(new AFunctionalInterface() { @Override public boolean anotherMethod(int i) { return i == 982 } }); 

Com expressões lambda:

 obj.aMethod(i -> i == 982); 

Aqui está um trecho do tutorial Java sobre Lambda Expressions :

Sintaxe das Expressões Lambda

Uma expressão lambda consiste no seguinte:

  • Uma lista separada por vírgula de parâmetros formais entre parênteses. O método CheckPerson.test contém um parâmetro, p, que representa uma instância da class Person.

    Nota : Você pode omitir o tipo de dados dos parâmetros em uma expressão lambda. Além disso, você pode omitir os parênteses se houver apenas um parâmetro. Por exemplo, a seguinte expressão lambda também é válida:

     p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() < = 25 
  • O símbolo da seta, ->

  • Um corpo, que consiste em uma única expressão ou um bloco de instruções. Este exemplo usa a seguinte expressão:

     p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() < = 25 

    Se você especificar uma única expressão, o Java Runtime avaliará a expressão e, em seguida, retornará seu valor. Como alternativa, você pode usar uma instrução de retorno:

     p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() < = 25; } 

    Uma declaração de retorno não é uma expressão; Em uma expressão lambda, você deve colocar instruções entre chaves ({}). No entanto, você não precisa include uma invocação de método inválido entre chaves. Por exemplo, o seguinte é uma expressão lambda válida:

     email -> System.out.println(email) 

Note que uma expressão lambda se parece muito com uma declaração de método; você pode considerar expressões lambda como methods anônimos - methods sem nome.


Aqui está como você pode "passar um método" usando uma expressão lambda:

 interface I { public void myMethod(Component component); } class A { public void changeColor(Component component) { // code here } public void changeSize(Component component) { // code here } } class B { public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) { for(Component leaf : myComponentArray) { if(leaf instanceof Container) { // recursive call if Container Container node = (Container)leaf; setAllComponents(node.getComponents(), myMethodInterface); } // end if node myMethodsInterface.myMethod(leaf); } // end looping through components } } class C { A a = new A(); B b = new B(); public C() { b.setAllComponents(this.getComponents(), component -> a.changeColor(component)); b.setAllComponents(this.getComponents(), component -> a.changeSize(component)); } } 

Use o object java.lang.reflect.Method e chame invoke

Primeiro defina uma interface com o método que você deseja passar como parâmetro

 public interface Callable { public void call(int param); } 

Implementar uma aula com o método

 class Test implements Callable { public void call(int param) { System.out.println( param ); } } 

// Invoque assim

 Callable cmd = new Test(); 

Isso permite que você passe cmd como parâmetro e invoque a chamada de método definida na interface

 public invoke( Callable callable ) { callable.call( 5 ); } 

A última vez que verifiquei, o Java não é capaz de fazer exatamente o que você quer; você tem que usar ‘work-around’ para contornar essas limitações. Tanto quanto eu vejo, interfaces são uma alternativa, mas não uma boa alternativa. Talvez quem te disse isso fosse algo assim:

 public interface ComponentMethod { public abstract void PerfromMethod(Container c); } public class ChangeColor implements ComponentMethod { @Override public void PerfromMethod(Container c) { // do color change stuff } } public class ChangeSize implements ComponentMethod { @Override public void PerfromMethod(Container c) { // do color change stuff } } public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { //recursive call if Container Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } //end if node myMethod.PerfromMethod(leaf); } //end looping through components } 

Que você então invocaria com:

 setAllComponents(this.getComponents(), new ChangeColor()); setAllComponents(this.getComponents(), new ChangeSize()); 

Embora isso ainda não seja válido para o Java 7 e abaixo, acredito que devemos olhar para o futuro e, pelo menos, reconhecer as mudanças que virão em novas versões, como o Java 8.

Ou seja, esta nova versão traz lambdas e referências a methods para Java (junto com novas APIs , que são outra solução válida para esse problema. Embora ainda precisem de uma interface, nenhum object novo é criado, e arquivos de classs extras não precisam poluir diretórios de saída devido a diferentes manipulação pela JVM.

Ambos os sabores (lambda e referência de método) requerem uma interface disponível com um único método cuja assinatura é usada:

 public interface NewVersionTest{ String returnAString(Object oIn, String str); } 

Nomes de methods não importarão daqui em diante. Onde um lambda é aceito, uma referência de método é também. Por exemplo, para usar nossa assinatura aqui:

 public static void printOutput(NewVersionTest t, Object o, String s){ System.out.println(t.returnAString(o, s)); } 

Esta é apenas uma simples invocação de interface, até que o lambda 1 seja passado:

 public static void main(String[] args){ printOutput( (Object oIn, String sIn) -> { System.out.println("Lambda reached!"); return "lambda return"; } ); } 

Isto irá produzir:

 Lambda reached! lambda return 

Referências de método são semelhantes. Dado:

 public class HelperClass{ public static String testOtherSig(Object o, String s){ return "real static method"; } } 

e principal:

 public static void main(String[] args){ printOutput(HelperClass::testOtherSig); } 

a saída seria real static method . As referências de método podem ser estáticas, instâncias, não-estáticas com instâncias arbitrárias e até mesmo construtores . Para o construtor, algo como ClassName::new seria usado.

1 Isso não é considerado um lambda por alguns, pois tem efeitos colaterais. Ele ilustra, no entanto, o uso de uma forma mais simples de visualizar.

Se você não precisar desses methods para retornar algo, poderá fazê-los retornar objects Runnable.

 private Runnable methodName (final int arg){ return new Runnable(){ public void run(){ // do stuff with arg } } } 

Então use-o como:

 private void otherMethodName (Runnable arg){ arg.run(); } 

Desde o Java 8 existe uma interface Function ( docs ), que possui método

 R apply(T t); 

Você pode usá-lo para passar funções como parâmetros para outras funções. T é o tipo de input da function, R é o tipo de retorno.

Em seu exemplo, você precisa passar uma function que usa o tipo Component como uma input e não retorna nada – Void . Neste caso, a Function não é a melhor escolha, já que não há autoboxing do tipo Void. A interface que você está procurando é chamada Consumer ( docs ) com o método

 void accept(T t); 

Ficaria assim:

 public void setAllComponents(Component[] myComponentArray, Consumer myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } myMethod.accept(leaf); } } 

E você chamaria isso usando referências de método:

 setAllComponents(this.getComponents(), this::changeColor); setAllComponents(this.getComponents(), this::changeSize); 

Supondo que você tenha definido os methods changeColor () e changeSize () na mesma class.


Se o seu método aceitar mais de um parâmetro, você pode usar BiFunction – T e U sendo os tipos de parâmetros de input e R sendo o tipo de retorno. Há também BiConsumer (dois argumentos, nenhum tipo de retorno). Infelizmente para 3 ou mais parâmetros de input, você precisa criar uma interface sozinho. Por exemplo:

 public interface Function4 { R apply(A a, B b, C c, D d); } 

Use o padrão Observer (às vezes também chamado padrão Listener):

 interface ComponentDelegate { void doSomething(Component component); } public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) { // ... delegate.doSomething(leaf); } setAllComponents(this.getComponents(), new ComponentDelegate() { void doSomething(Component component) { changeColor(component); // or do directly what you want } }); 

new ComponentDelegate()... declara um tipo anônimo implementando a interface.

Java tem um mecanismo para passar o nome e chamá-lo. Faz parte do mecanismo de reflection. Sua function deve ter um parâmetro adicional da class Method.

 public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled) { ... Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist); ... } 

Aqui está um exemplo básico:

 public class TestMethodPassing { private static void println() { System.out.println("Do println"); } private static void print() { System.out.print("Do print"); } private static void performTask(BasicFunctionalInterface functionalInterface) { functionalInterface.performTask(); } @FunctionalInterface interface BasicFunctionalInterface { void performTask(); } public static void main(String[] arguments) { performTask(TestMethodPassing::println); performTask(TestMethodPassing::print); } } 

Saída:

 Do println Do print 

Eu não sou um especialista em java, mas eu resolvo seu problema assim:

 @FunctionalInterface public interface AutoCompleteCallable { String call(T model) throws Exception; } 

Eu defino o parâmetro na minha interface especial

 public  void initialize(List entries, AutoCompleteCallable getSearchText) {....... //call here String value = getSearchText.call(item); ... } 

Finalmente, eu implemento o método getSearchText ao chamar o método initialize .

 initialize(getMessageContactModelList(), new AutoCompleteCallable() { @Override public String call(Object model) throws Exception { return "custom string" + ((xxxModel)model.getTitle()); } }) 

Eu não acho que lambdas são feitos para isso … Java não é uma linguagem de functional programming e nunca será assim, nós não passamos methods como parâmetros. Dito isso, lembre-se de que Java é orientado a objects e, com isso em mente, podemos fazer o que quisermos. A primeira ideia é simplesmente passar um “object que contém um método” como um parâmetro. Então, sempre que você precisar “passar” o método, apenas passe uma instância dessa class. Observe que quando você define o método, você deve adicionar como parâmetro uma instância da class que contém o método. Isso deve funcionar, mas não é o que queremos porque você não pode redefinir o método, a menos que tenha access ao código de class e isso não seja possível em muitos casos; Além disso, acho que se alguém precisa passar um método como parâmetro, é porque o comportamento do método deve ser dynamic. O que quero dizer é que o programador que usa suas classs deve ser capaz de escolher o que o método deve retornar, mas não o seu tipo. Felizmente para nós o Java tem uma solução bonita e simples: classs abstratas. Classes abstratas, em poucas palavras, são usadas quando você conhece a “assinatura” de um método “mas não seu comportamento … Você pode envolver o nome e o tipo do método em uma class abstrata e passar uma instância dessa class como uma class abstrata”. parâmetro para o método … Espere … não é a mesma coisa que antes? E você pode ter uma instância de uma class abstrata? Não e Não … mas também sim … quando você cria um método abstrato você também tem que redefini-la em uma class que estende a class abstrata e, por causa da binding dinâmica do Java, o Java sempre usará a versão redefinida (a menos que você a declare estática, privada e algumas outras coisas). Suponha que desejamos aplicar uma function a uma matriz de números: portanto, se quisermos fazer um quadrado, a input-saída deve ser assim [1,2,3,4, …] -> [1,4,9,16 , … (em uma linguagem de functional programming como o haskell isso é algo fácil graças a algumas ferramentas como ‘map’, …). Note que não há nada de especial em quadratura de números, podemos aplicar qualquer function que quisermos t. Então o código deve ser algo como isto [args], f -> [f (args)]. De volta a java => uma function é apenas um método, então o que queremos é uma function que aplique outra function a um array. Em suma, precisamos passar um método como um parâmetro. Aqui está como eu faria ==>

1) DEFINIR A CLASSE ABSTRATA DO ENVOLTÓRIO E O MÉTODO

 public abstract class Function { public abstract double f(double x); } 

2) DEFINIR A CLASSE COM O MÉTODO APPLY_TO_ARRAY

 public class ArrayMap { public static double[] apply_to_array(double[] arr, Function fun) { for(int i=0; i 

3) CRIAR UMA CLASSE DE TESTER E TENHA ALGUM DIVERTIMENTO

 public class Testclass extends Function { public static void main(String[] args) { double[] myarr = {1,2,3,4}; ArrayMap.apply_to_array(myarr, new Testclass()); for (double k : myarr) { System.out.println(k); } } @Override public double f(double x) { return Math.log(x); } } 

Note que precisamos passar um object do tipo Function e como o Testclass estende a class Function que podemos usar, o cast é automático.