Вопрос-ответ

How to pass a function as a parameter in Java? [duplicate]

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

В Java, как можно передать функцию в качестве аргумента другой функции?

Переведено автоматически
Ответ 1

Java 8 и выше

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

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.

Ответ 2

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

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

}
Ответ 3

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

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

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

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

С помощью лямбда-выражений:

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

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


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


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



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

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


    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)


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



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

Примечание: здесь используется новый стандартный функциональный интерфейс, 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);
}
Ответ 4

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

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

Так, например, в commons-collections есть org.apache.commons.collections.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 устарел. Я просто забыл эквивалент в Guava.

java