Как передать функцию в качестве параметра в Java? [дубликат]

этот вопрос уже есть ответ здесь:

можно ли передать метод в метод Java в качестве параметра? Если да, то кто-нибудь может меня проводить? Это не кажется тривиальным

8 ответов


Java 8 и выше

используя Java 8+ лямбда-выражения, если у вас есть класс или интерфейс только с одним методом, например:

public interface MyInterface {
    String doSomething(int param1, String param2);
}

потом в любом месте, где используется MyInterface, вы можете заменить лямбда-выражение:

class MyClass {
    public MyInterface myInterface = (p1, p2) -> { return p2 + p1; };
}

например, вы можете создать новый поток очень быстро:

new Thread(() -> someMethod()).start();

и с помощью синтаксис ссылки на метод чтобы сделать его еще уборщик:

new Thread(this::someMethod).start();

без лямбда-выражения, эти последние два примера будет выглядеть так:

new Thread(new Runnable() { someMethod(); }).start();

Перед Java 8

общим шаблоном было бы "обернуть" его в интерфейсе, например Callable, например, затем вы переходите в вызываемое:

public T myMethod(Callable<T> func) {
    return func.call();
}

этот шаблон известен как Команда.

имейте в виду, что вам лучше всего создать интерфейс для вашего конкретного использования. Если вы решили пойти с callable, то вы замените T выше любым типом возвращаемого значения, которое вы ожидаете, например String.

в ответ на ваш комментарий ниже, можно сказать:

public int methodToPass() { 
        // do something
}

public void dansMethod(int i, Callable<Integer> myFunc) {
       // do something
}

затем вызовите его, возможно, используя анонимный внутренний класс:

dansMethod(100, new Callable<Integer>() {
   public Integer call() {
        return methodToPass();
   }
});

имейте в виду, что это не трюк. Это просто базовый концептуальный эквивалент java для указателей функций.


для этого можно использовать отражение Java. Метод будет представлен как экземпляр java.ленг.отражать.Метод.

import java.lang.reflect.Method;

public class Demo {

    public static void main(String[] args) throws Exception{
        Class[] parameterTypes = new Class[1];
        parameterTypes[0] = String.class;
        Method method1 = Demo.class.getMethod("method1", parameterTypes);

        Demo demo = new Demo();
        demo.method2(demo, method1, "Hello World");
    }

    public void method1(String message) {
        System.out.println(message);
    }

    public void method2(Object object, Method method, String message) throws Exception {
        Object[] parameters = new Object[1];
        parameters[0] = message;
        method.invoke(object, parameters);
    }

}

Лямбда-Выражения

добавить в jk.'ы отличный ответ, теперь вы можете передать метод более легко с помощью Лямбда-Выражения (в Java 8). Во-первых, некоторые предпосылки. А функционального интерфейса - это интерфейс, который имеет один и только один абстрактный метод, хотя он может содержать любое число методы по умолчанию (новое в Java 8) и статические методы. Лямбда-выражение может быстро реализовать абстрактный метод, без всего ненужного синтаксиса, необходимого, если вы не используете лямбда-выражение.

без лямбда-выражений:

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

С лямбда-выражениями:

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

вот выдержка из учебник Java по лямбда-выражениям:

синтаксис лямбда-выражений

лямбда-выражение состоит из следующего:

  • A разделенный запятыми список формальных параметров, заключенный в круглые скобки. В CheckPerson.метод теста содержит один параметр, p, который представляет собой экземпляр класса человек.

    Примечание: ты можно опустить тип данных параметров в лямбда-выражении. В кроме того, вы можете опустить скобки, если есть только один параметр. Например, также допустимо следующее лямбда-выражение:

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    
  • маркер со стрелкой , ->

  • тело, которое состоит из одного выражения или блока выражений. В этом примере используется следующее выражение:

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

    если указать одно выражение, то среда выполнения Java вычисляет выражение и возвращает его значение. Альтернативно, вы можете использовать оператор return:

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

    оператор return не является выражением; в лямбда-выражении необходимо заключить операторы в фигурные скобки ({}). Однако у вас нет заключать вызов метода void в фигурные скобки. Например, ниже приведено допустимое лямбда-выражение:

    email -> System.out.println(email)
    

обратите внимание, что лямбда-выражение очень похоже на объявление метода ; лямбда-выражения можно рассматривать как анонимные методы-методы без имени.


вот как вы можете "передать метод", используя лямбда-выражение:

Примечание: это использует новый стандарт функциональный интерфейс, java.util.function.IntConsumer.

class A {
    public static void methodToPass(int i) { 
        // do stuff
    }
}
import java.util.function.IntConsumer;

class B {
    public void dansMethod(int i, IntConsumer aMethod) {
        /* you can now call the passed method by saying aMethod.accept(i), and it
        will be the equivalent of saying A.methodToPass(i) */
    }
}
class C {
    B b = new B();

    public C() {
        b.dansMethod(100, j -> A.methodToPass(j));   //Lambda Expression here
    }
}

приведенный выше пример можно сократить еще больше, используя :: оператор.

public C() {
    b.dansMethod(100, A::methodToPass);
}

благодаря Java 8 вам не нужно делать следующие шаги, чтобы передать функцию методу, вот для чего предназначены лямбды, см. лямбда-выражение Oracle учебник. Остальная часть этого сообщения описывает, что нам приходилось делать в старые добрые времена, чтобы реализовать эту функциональность.

обычно вы объявляете свой метод как принимающий некоторый интерфейс с одним методом, а затем передаете объект, который реализует этот интерфейс. Пример находится в commons-collections, где у вас есть интерфейсы для замыкания, трансформатора и предиката и методы, в которые вы передаете реализации этих. Guava-это новые улучшенные общие коллекции, вы можете найти там эквивалентные интерфейсы.

Так, например, commons-collections имеет org.апаш.палата общин.коллекции.CollectionUtils, который имеет множество статических методов, которые принимают переданные объекты, чтобы выбрать один наугад, есть один, называемый exists с этой сигнатурой:

static boolean exists(java.util.Collection collection, Predicate predicate) 

Он принимает объект это реализует предикат интерфейса, что означает, что на нем должен быть метод, который принимает некоторый объект и возвращает логическое значение.

Так я могу назвать это так:

CollectionUtils.exists(someCollection, new Predicate() {
    public boolean evaluate(Object object) { 
        return ("a".equals(object.toString());
    }
});

и это возвращает true или false в зависимости от того,someCollection содержит объект, для которого предикат возвращает true.

В любом случае, это всего лишь пример, и commons-collections устарели. Я просто забыл эквивалент в гуаве.


Java поддерживает закрытие просто отлично. Он просто не поддерживает функции, поэтому синтаксис, к которому вы привыкли для замыканий, гораздо более неудобный и громоздкий: вы должны обернуть все в классе с помощью метода. Например,

public Runnable foo(final int x) {
  return new Runnable() {
    public void run() {
      System.out.println(x);
    }
  };
}

вернет Runnable объект, чей run() метод "закрывает"x передано, как и на любом языке, который поддерживает первоклассные функции и замыкания.


я использовал шаблон команды, который @jk. упомянуто, добавив тип возврата:

public interface Callable<I, O> {

    public O call(I input);

}

Я знаю, что это довольно старый пост, но у меня есть другое немного более простое решение. Вы можете создать другой класс внутри и сделать его абстрактным. Затем сделайте абстрактный метод, назовите его как угодно. В исходном классе создайте метод, который принимает новый класс в качестве параметра, в этом методе вызовите абстрактный метод. Это будет выглядеть примерно так.

public class Demo {

public Demo(/.../){

}


public void view(Action a){
    a.preform();
}


/**
 * The Action Class is for making the Demo
 * View Custom Code
 */
public abstract class Action {

    public Action(/.../){

    }

    abstract void preform();
}
}

теперь вы можете сделать что-то вроде этого, чтобы вызвать метод из класса.

/...
Demo d = new Demo;
Action a = new Action() {

    @Override
    void preform() {
        //Custom Method Code Goes Here
    }
};

/.../
d.view(a)

Как я сказал Я знаю его старый, но так, я думаю, немного легче. Надеюсь, это поможет.


Java (пока) не поддерживает закрытие. Но есть и другие языки, такие как Scala и Groovy, которые работают в JVM и поддерживают закрытие.